Warum können Eigenschaften nicht implizit in Delegaten konvertiert werden?

9

Wir alle wissen, dass Eigenschaften in C # nach einfachen alten Methoden kompiliert werden. Im Gegensatz zu Methoden (-gruppen) können sie jedoch nicht als Argumente für andere Methoden angegeben werden, die einen Delegaten wie ein Func<T>oder Action<T>(Getter und Setter) erwarten . Gibt es etwas Besonderes, das dies verbietet, oder ist es nur eine Funktion, die nicht "kostenlos" war, wenn Methodengruppen implizit in Delegate konvertierbar gemacht wurden?

Beispielcode:

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

Wenn ich raten müsste, würde ich sagen, dass das syntaktische Problem der Unterscheidung zwischen Aufrufen und Verweisen auf die Eigenschaft selbst keine Lösung wert war, wenn wir nur den zusätzlichen Fuzz des Schreibens () => MyClass.Xoder x => MyClass.X = x.

Sara
quelle
Welche würden sie anrufen? Holen Sie sich oder setzen Sie
Ewan
Mein Gedanke wäre, dass es vom Delegatentyp abhängt. setist ein Actionund getist ein Funczum Beispiel
sara
1
Zu Ihrer Information, Syntax unter Verwendung von Inline-Delegaten zum Übergeben von Getter / Setter . delegate {return SomeProperty}geht an Getter vorbei; delegate(YourType value) {SomeProperty = value}Setter passiert. Wenn Sie nur ein oder zwei Stellen übergeben müssen, ist dies eine Abkürzung für die umschlossenen Methoden, die Sie sonst erstellen würden.
ToolmakerSteve
Wenn Sie in FWIW sowohl Getter als auch Setter übergeben möchten, sollten Sie stattdessen eine Interfacemit dieser Eigenschaft definieren und die Signatur der aufgerufenen Methode ändern, um diese Schnittstelle als Parameter zu verwenden.
ToolmakerSteve

Antworten:

7

Das Problem ist, dass "MyClass.X" eine genau definierte Bedeutung hat, nämlich den Getter für die Eigenschaft "X" aufzurufen. Es ist wichtig zu beachten, dass trotz des Aufrufs einer Methode keine Klammern erforderlich sind.

Auf der anderen Seite, wenn eine reguläre Methode aufrufen, wenn Sie beispielsweise „Foo“ in Ihrem Beispiel - Code aufrufen, Klammern sind erforderlich , um anzuzeigen , dass das Verfahren ausgeführt werden soll. Wenn Sie einen Verweis auf eine Methode übergeben möchten, schließen Sie die Klammern aus (siehe Beispiel unten).

Dies bedeutet, dass beim Verweisen auf eine Methode keine Mehrdeutigkeit besteht - entweder führen Sie sie sofort aus (Übergabe aller erforderlichen Argumente und einschließlich Klammern) oder Sie übergeben die Methode als Argument (keine Klammern).

Bei einem Property Getter oder Setter bedeutet keine Klammer bereits "Getter / Setter sofort ausführen". Wenn der Property Getter / Setter auch als Methodengruppe übergeben werden könnte, würde "x.Name" manchmal "Name Getter jetzt ausführen" und manchmal "Übergeben des Namens Getter als Methodengruppe" bedeuten. Möglicherweise ist es möglich, den Compiler so zu ändern, dass er eindeutig ist, je nachdem, ob "x.Name" an eine Funktion übergeben wird, die eine Zeichenfolge erwartet (in diesem Fall würde der Getter ausgeführt) oder ob er an übergeben wird Als Funktion, die einen Func <string> erwartet (in diesem Fall würde der Getter als Methodengruppe übergeben), versucht das C # -Sprachteam, diese Art von möglicherweise verwirrenden Szenarien zu vermeiden.

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

Aufgrund der Grundlagen der Funktionsweise von Grammatik und Compiler ist dies nicht möglich.

Ausdrücke werden "bottom up" MyClass.Xausgewertet, dh der Ausdruck wird auf seinen int-Wert ausgewertet, bevor dieses Ergebnis als Argument für die Funktion eingegeben wird. Ihr Vorschlag scheint darauf hinzudeuten, dass der Kontext - der Typ des Parameters - bestimmen könnte, ob der Ausdruck als Getter-Aufruf oder als Referenz zur Getter-Methode selbst interpretiert werden soll. Dies ist nicht eindeutig möglich. Was ist, wenn der Typ der Immobilie selbst ein Funcoder ist Action? Und wie wird var x = MyClass.Xdas interpretiert? Was ist, wenn die Methode Überladungen mit entweder intoder Funcoder ActionParametern aufweist?

Um diese Mehrdeutigkeiten aufzulösen, benötigen Sie eine Unterscheidung auf Syntaxebene, z. B. einen Operator mit besonderer Unterstützung in der Grammatik wie den typeofOperator, z.

Foo(getterof(MyClass.X));

Dies wird jedoch für eine Funktion mit solch begrenztem Nutzen viel zu viel Mühe bedeuten.

JacquesB
quelle
Ja, mit Subtilität könnte man arbeiten, wenn man muss, während Unentscheidbarkeit ein Show-Stopper ist.
Deduplikator
2

Foo.Do()Das heißt, der Aufruf und das Foo.DoSein des Delegierten ist in Ordnung.

Foo.XEs ist verwirrend , sowohl die Eigenschaft als auch der Getter-Delegierte und der Setter-Delegierte zu sein, abhängig von subtilen Kontextunterschieden. Gute Funktionen, die nicht überwältigend gut sind, kommen nicht einmal in C #, dies scheint eine wirklich schlechte zu sein. Wenn Sie einmal kurz Code schreiben möchten, versuchen Sie es mit Perl.

Es gibt bereits eine Möglichkeit, klar auszudrücken, welche Sie in C # benötigen. Ich sehe das Problem nicht.

Nathan Cooper
quelle