14

We all know that properties in C# get compiled to actual plain old methods. But unlike method(-group)s, they can't be given as arguments to other methods that expect a delegate like a Func<T> or Action<T> (getter and setter). Is there anything special prohibiting this, or is it just a feature that didn't come "for free" when making method groups implicitly convertible to delegates?

Example code:

public class MyClass
{
    public static int X { get; set; }
}

private static void Foo<T>(Func<T> f)
{
    Console.WriteLine(f());
}

private static void Bar<T>(Action<T> f)
{
    f(default(T));
}

private static void Test()
{
    // neither of these lines compile, even with an explicit type-cast.
    Foo(MyClass.X);
    Bar(MyClass.X);
}

If I had to guess, I'd say that the syntactical problem of differentiating between invocations and references to the property itself wasn't worth solving when we can just do the extra fuzz of writing () => MyClass.X or x => MyClass.X = x.

5
  • 1
    Which would they call? Get or set
    – Ewan
    Commented Jun 19, 2016 at 8:25
  • My thought would be that it depends on the delegate type. set is an Action and get is a Func for example
    – sara
    Commented Jun 19, 2016 at 8:54
  • 1
    FYI, syntax using inline delegates to pass getter/setter. delegate {return SomeProperty} passes getter; delegate(YourType value) {SomeProperty = value} passes setter. If you only need to pass one or two places, this is shorthand for the wrapped methods you would otherwise create. Commented Sep 21, 2018 at 17:45
  • And FWIW, if you find yourself wanting to pass both getter and setter, consider instead defining an Interface with that property, and changing the signature of the called method to take that interface as a parameter. Commented Sep 21, 2018 at 17:56
  • @ToolmakerSteve Doing it like that will create a new separate function inside the generated IL which does the wrapping call, which just seems wasteful. There's no reason the C# compiler couldn't have the Func point directly to the property's getter method (the hidden get_PropertyName method you see in ILSpy) - which would avoid the extra indirect CALL jump.
    – Dai
    Commented Mar 11, 2022 at 2:23

3 Answers 3

11

The problem is that "MyClass.X" has a well-defined meaning, which is to call the getter on the property "X". The important thing to note is that, even though a method is being called, brackets are not required.

On the other hand, when calling a regular method, such as when you call "Foo" in your example code, brackets are required to indicate that the method should be executed. If you wanted to pass a reference to a method then you would exclude the brackets (see example below).

This means that there is no ambiguity when referencing a method - either you are executing it immediately (passing any arguments that it requires and including brackets) or you are passing the method as an argument (no brackets).

With a property getter or setter, no brackets already means "execute getter/setter immediately". If the property getter/setter could also be passed as a method group then sometimes "x.Name" would mean "execute the Name getter now" and sometimes it would mean "pass the Name getter as a method group". While it might be possible to change the compiler to disambiguate based upon whether "x.Name" appears to be getting passed into a function that expects a string (in which case the getter would be executed) or whether it appears to be getting passed into a function that expects a Func<string> (in which case the getter would be passed as a method group), the C# language team try to avoid these sorts of potentially-confusing scenarios.

using System;

namespace Example
{
    public static class Program
    {
        static void Main()
        {
            ValueWriter(1); // Execute ValueWriter (because brackets)
            Bar(ValueWriter); // Pass ValueWriter as an action (no brackets)
        }

        private static void Bar(Action<int> f)
        {
            f(2);
        }

        private static void ValueWriter(int value)
        {
            Console.WriteLine("Value is " + value);
        }
    }
}
8

It is not possible due to the fundamentals of how the grammar and compiler works.

Expressions are evaluated "bottom up", which means the expression MyClass.X evaluates to its int value before this result is fed as an argument the function. Your proposal seem to suggest that the context - the type of the parameter - could direct if the expression should be interpreted as a getter invocation or as a refence to the getter method itself. This is not possible to do in an unambigous way. What if the type of the property itself is a Func or Action? And how will var x = MyClass.X be interpreted? What if the method has overloads with either int or Func or Action parameters?

To resolve these ambiguities you need a distinction at the syntax level, e.g an operator with special support in the grammar like the typeof operator, eg:

Foo(getterof(MyClass.X));

But this will be far to much trouble for a feature with such limited benefit.

1
  • Yes, subtlety one could work with, if one must, while undecidability is a show-stopper. Commented Sep 24, 2018 at 15:54
2

Foo.Do() meaning the invocation and Foo.Do being the,delegate is fine.

Foo.X being the property as well as the getter delegate and the setter delegate depending on subtle context differences is confusing. Good features that aren't overwhelming good don't even get into C#, this seems like an actual bad one. If you want short write once code try Perl.

There already exists a way to clearly express which one you need in C#, I don't see the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.