Perché le proprietà non sono convertibili implicitamente in delegati

7

Sappiamo tutti che le proprietà in C # vengono compilate secondo i vecchi e normali metodi. Ma a differenza del metodo (-group) s, non possono essere dati come argomenti ad altri metodi che si aspettano un delegato come Func<T> o Action<T> (getter e setter). C'è qualcosa di speciale che vieta questo, o è solo una caratteristica che non è venuta "gratis" quando si rendono i gruppi di metodi implicitamente convertibili ai delegati?

Codice di esempio:

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

Se dovessi indovinare, direi che il problema sintattico di differenziare tra invocazioni e riferimenti alla proprietà stessa non valeva la pena di risolverlo quando possiamo semplicemente fare il fuzz extra di scrivere () => MyClass.X o x => MyClass.X = x .

    
posta sara 19.06.2016 - 09:42
fonte

3 risposte

6

Il problema è che "MyClass.X" ha un significato ben definito, che è quello di chiamare il getter sulla proprietà "X". La cosa importante da notare è che, anche se viene chiamato un metodo, non sono richieste parentesi.

D'altra parte, quando si chiama un metodo normale, come quando si chiama "Foo" nel codice di esempio, le parentesi sono richieste per indicare che il metodo deve essere eseguito. Se si desidera passare un riferimento a un metodo, si escluderebbero le parentesi (vedere l'esempio di seguito).

Ciò significa che non vi è alcuna ambiguità quando si fa riferimento a un metodo - o lo si sta eseguendo immediatamente (passando tutti gli argomenti che richiede e tra parentesi) o si passa il metodo come argomento (nessuna parentesi).

Con un getter o setter di proprietà, nessuna parentesi significa "esegui immediatamente getter / setter". Se il getter / setter della proprietà potrebbe anche essere passato come un gruppo di metodi, a volte "x.Name" significherebbe "eseguire il getter del nome ora" e talvolta significherebbe "passare il nome getter come un gruppo di metodi". Mentre potrebbe essere possibile cambiare il compilatore per disambiguare in base al fatto che "x.Name" sembra essere passato in una funzione che si aspetta una stringa (nel qual caso il getter sarebbe eseguito) o se sembra essere passato in una funzione che si aspetta un Func < string > (nel qual caso il getter sarebbe passato come un gruppo di metodi), il team di linguaggio C # cerca di evitare questo genere di scenari potenzialmente confusi.

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);
        }
    }
}
    
risposta data 19.06.2016 - 12:48
fonte
5

Non è possibile a causa dei fondamenti di come funzionano la grammatica e il compilatore.

Le espressioni sono valutate "dal basso verso l'alto", il che significa che l'espressione MyClass.X valuta il suo valore int prima questo risultato è alimentato come argomento della funzione. La tua proposta sembra suggerire che il contesto - il tipo del parametro - potrebbe dirigere se l'espressione deve essere interpretata come una chiamata getter o come un riferimento al metodo getter stesso. Questo non è possibile fare in modo non ambiguo. Cosa succede se il tipo della proprietà stessa è un Func o Action ? E come verrà interpretato var x = MyClass.X ? Cosa succede se il metodo ha sovraccarichi con i parametri int o Func o Action ?

Per risolvere queste ambiguità è necessaria una distinzione a livello di sintassi, ad esempio un operatore con supporto speciale nella grammatica come l'operatore typeof , ad esempio:

Foo(getterof(MyClass.X));

Ma questo sarà di gran lunga un problema per una funzionalità con un beneficio così limitato.

    
risposta data 19.06.2016 - 13:24
fonte
2

Foo.Do() che indica l'invocazione e Foo.Do è il, delegato va bene.

Foo.X è la proprietà e il delegato getter e il delegato setter a seconda delle sottili differenze di contesto è confuso. Le buone caratteristiche che non sono travolgenti non entrano nemmeno in C #, sembra una cattiva realtà. Se vuoi scrivere brevemente una volta che il codice prova Perl.

Esiste già un modo per esprimere chiaramente quello che ti serve in C #, non vedo il problema.

    
risposta data 19.06.2016 - 11:31
fonte

Leggi altre domande sui tag