web-dev-qa-db-ja.com

プロパティが暗黙的にデリゲートに変換できないのはなぜですか

C#のプロパティが実際の単純な古いメソッドにコンパイルされることは誰でも知っています。ただし、me​​thod(-group)とは異なり、Func<T>またはAction<T>(getterおよびsetter)のようなデリゲートを期待する他のメソッドへの引数としてそれらを与えることはできません。これを禁止する特別なものはありますか、それともメソッドグループをデリゲートに暗黙的に変換可能にするときに「無料」で提供されなかった機能だけですか?

コード例:

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

推測しなければならないのですが、() => MyClass.Xまたはx => MyClass.X = xを書くだけのファズを行うことができる場合、呼び出しとプロパティ自体への参照を区別する構文上の問題は解決する価値がなかったと思います。

9
sara

問題は、「MyClass.X」が明確に定義された意味を持っていることです。これは、プロパティ「X」でゲッターを呼び出すことです。注意すべき重要なことは、メソッドが呼び出されていても、ブラケットは必要ないということです。

一方、コード例で "Foo"を呼び出す場合など、通常のメソッドを呼び出す場合は、メソッドを実行する必要があることを示すために角かっこareが必要です。メソッドへの参照を渡したい場合は、括弧を除外します(以下の例を参照)。

これは、メソッドを参照するときに曖昧さがないことを意味します。つまり、メソッドをすぐに実行する(必要な引数を渡し、ブラケットを含む)か、メソッドを引数として渡します(ブラケットなし)。

プロパティのゲッターまたはセッターでは、角括弧は「ゲッター/セッターをすぐに実行する」ことを意味していません。プロパティgetter/setterもメソッドグループとして渡すことができる場合、「x.Name」は「Name getterを今すぐ実行する」ことを意味し、「Name getterをメソッドグループとして渡す」ことを意味する場合があります。 "x.Name"が文字列を期待する関数に渡されているように見える(その場合、ゲッターが実行される)か、または渡されているように見えるかどうかに基づいて、コンパイラを明確にすることが可能かもしれません。 Func <string>を期待する関数(この場合、getterはメソッドグループとして渡されます)、C#言語チームは、このような混乱の可能性があるシナリオを回避しようとします。

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

文法とコンパイラがどのように機能するかの基本的な理由により、それは不可能です。

式は「ボトムアップ」で評価されます。つまり、式MyClass.Xはそのint値に評価されますbeforeこの結果は関数の引数として渡されます。あなたの提案は、コンテキスト-パラメータのタイプ-が、式がゲッターの呼び出しとして解釈されるか、ゲッターメソッド自体への参照として解釈されるかを指示する可能性があることを示唆しているようです。これは明確な方法で行うことはできません。プロパティ自体のタイプがFuncまたはActionの場合はどうなりますか?そして、var x = MyClass.X解釈されますか?メソッドにintFunc、またはActionパラメータのいずれかのオーバーロードがある場合はどうなりますか?

これらのあいまいさを解決するには、構文レベルでの区別が必要です。たとえば、typeof演算子のように文法で特別にサポートされている演算子。

Foo(getterof(MyClass.X));

しかし、これは、そのような限定された利点を持つ機能にとってははるかに問題になります。

5
JacquesB

Foo.Do()は呼び出しを意味し、Foo.Do代理人であることは問題ありません。

Foo.Xはプロパティであり、ゲッターデリゲートとセッターデリゲートである微妙なコンテキストの違いによるは混乱を招きます。圧倒的ではない優れた機能は、C#にも組み込まれていません。これは実際には悪い機能のようです。コードを1回だけ記述したい場合は、Perlを試してください。

C#で必要なものを明確に表現する方法は既に存在しますが、問題はありません。

2
Nathan Cooper