Why aren’t properties implicitly convertible to delegates

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

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

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

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.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *