Por que as propriedades não são implicitamente conversíveis em delegados


9

Todos sabemos que as propriedades em C # são compiladas para métodos antigos simples e reais. Mas, diferentemente dos métodos (-group) s, eles não podem ser dados como argumentos para outros métodos que esperam um delegado como a Func<T>ou Action<T>(getter e setter). Existe algo especial que proíba isso ou é apenas um recurso que não veio "de graça" ao tornar os grupos de métodos implicitamente conversíveis em delegados?

Código de exemplo:

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 eu tivesse que adivinhar, eu diria que o problema sintático de diferenciar entre invocações e referências à propriedade em si não valia a pena resolver quando podemos apenas fazer a confusão extra de escrever () => MyClass.Xou x => MyClass.X = x.


Como eles chamariam? Get or set
Ewan

Meu pensamento seria que isso depende do tipo de delegado. seté um Actione geté um Func, por exemplo,
sara

1
FYI, sintaxe usando delegados embutidos para passar o getter / setter . delegate {return SomeProperty}passa getter; delegate(YourType value) {SomeProperty = value}passa setter. Se você precisar passar apenas por um ou dois lugares, esta é uma abreviação para os métodos agrupados que você criaria.
Home

E o FWIW, se você deseja passar o getter e o setter, considere definir um Interfacecom essa propriedade e alterar a assinatura do método chamado para usar essa interface como parâmetro.
ToolmakerSteve

Respostas:


7

O problema é que "MyClass.X" tem um significado bem definido, que é chamar o getter na propriedade "X". O importante a ser observado é que, mesmo que um método esteja sendo chamado, os colchetes não são necessários.

Por outro lado, ao chamar um método regular, como quando você chama "Foo" no código de exemplo, são necessários colchetes para indicar que o método deve ser executado. Se você deseja passar uma referência a um método, exclua os colchetes (veja o exemplo abaixo).

Isso significa que não há ambiguidade ao fazer referência a um método - ou você o está executando imediatamente (passando os argumentos necessários e incluindo colchetes) ou o método como argumento (sem colchetes).

Com um getter ou setter de propriedade, nenhum colchete já significa "executar o getter / setter imediatamente". Se o getter / setter da propriedade também pudesse ser transmitido como um grupo de métodos, às vezes "x.Name" significaria "executar o getter de nome agora" e, às vezes, "passar o getter de nome como um grupo de métodos". Embora seja possível alterar o compilador para desambiguar com base em se "x.Name" parece estar sendo passado para uma função que espera uma string (nesse caso, o getter seria executado) ou se ele parece estar sendo passado para uma função que espera uma Func <string> (nesse caso, o getter seria passado como um grupo de métodos), a equipe de linguagem C # tenta evitar esse tipo de cenário potencialmente confuso.

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

5

Não é possível devido aos fundamentos de como a gramática e o compilador funcionam.

As expressões são avaliadas "de baixo para cima", o que significa que a expressão é MyClass.Xavaliada como seu valor int antes que esse resultado seja alimentado como argumento da função. Sua proposta parece sugerir que o contexto - o tipo do parâmetro - pode direcionar se a expressão for interpretada como uma chamada getter ou como uma referência ao próprio método getter. Isso não é possível de forma inequívoca. E se o tipo da propriedade em si for um Funcou Action? E como será var x = MyClass.Xinterpretado? E se o método tem sobrecargas tanto com intou Funcou Actionparâmetros?

Para resolver essas ambiguidades, você precisa de uma distinção no nível da sintaxe, por exemplo, um operador com suporte especial na gramática, como o typeofoperador, por exemplo:

Foo(getterof(MyClass.X));

Mas isso vai causar muitos problemas para um recurso com benefício tão limitado.


Sim, sutileza com a qual se poderia trabalhar, se for necessário, enquanto a indecidibilidade é um obstáculo.
Deduplicator

2

Foo.Do()significando a invocação e Foo.Dosendo o delegado, tudo bem.

Foo.Xsendo a propriedade assim como o delegado getter e o delegado setter, dependendo das diferenças sutis de contexto, é confuso. Os bons recursos que não são muito bons nem mesmo entram em C #, isso parece um péssimo. Se você quiser escrever uma vez, tente o código Perl.

Já existe uma maneira de expressar claramente qual você precisa em C #, não vejo o problema.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.