C# – Why aren’t properties implicitly convertible to delegates

cdelegatesproperties

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.

Best Answer

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);
        }
    }
}
Related Topic