Eu não entendo este caso:
public delegate int test(int i);
public test Success()
{
Func<int, int> f = x => x;
return f.Invoke; // <- code successfully compiled
}
public test Fail()
{
Func<int, int> f = x => x;
return f; // <- code doesn't compile
}
Por que a compilação está correta quando uso o Invoke
método e não correta quando retorno csharp Func<int,int>
diretamente?
delegate void test1(int i);
edelegate void test2(int i);
Respostas:
Há duas coisas que você precisa saber para entender esse comportamento.
System.Delegate
, mas delegados diferentes têm tipos diferentes e, portanto, não podem ser atribuídos um ao outro.Como delegados diferentes têm tipos diferentes, isso significa que você não pode atribuir um delegado de um tipo para outro.
Por exemplo, dado:
Então:
A primeira linha acima compila OK porque está usando o tratamento especial para atribuir um lambda ou um método a um delegado.
De fato, essa linha é efetivamente reescrita assim pelo compilador:
A segunda linha acima não é compilada porque está tentando atribuir uma instância de um tipo a outro tipo incompatível.
No que diz respeito aos tipos, não há atribuição compatível entre
test1
etest2
porque são tipos diferentes.Se ajudar a pensar sobre isso, considere esta hierarquia de classes:
O código a seguir não irá compilar, embora
Test1
eTest2
derivam da mesma classe base:Isso explica por que você não pode atribuir um tipo de delegado a outro. Essa é apenas a linguagem C # normal.
No entanto, o importante é entender por que você pode atribuir um método ou lambda a um representante compatível. Como observado acima, isso faz parte do suporte ao idioma C # para delegados.
Então, finalmente, para responder à sua pergunta:
Quando você usa,
Invoke()
está atribuindo uma chamada de MÉTODO ao delegado, usando a manipulação de linguagem C # especial para atribuir métodos ou lambdas a um delegado, em vez de tentar atribuir um tipo incompatível - portanto, ele compila OK.Para ser completamente claro, o código que é compilado no seu OP:
Na verdade, é convertido conceitualmente para algo como:
Enquanto o código com falha está tentando atribuir entre dois tipos incompatíveis:
fonte
No segundo caso,
f
é do tipoFunc<int, int>
, mas diz-se que o método retorna atest
. Como são tipos não relacionados (delegados), que não são conversíveis entre si, ocorre um erro do compilador. Você pode ir para esta seção das especificações de idioma e procurar "delegar". Você não encontrará menção de conversões entre delegados com as mesmas assinaturas.No primeiro caso, no entanto,
f.Invoke
é uma expressão de grupo de métodos , que na verdade não possui um tipo. O compilador C # converterá expressões de grupo de métodos em tipos de delegados específicos de acordo com o contexto, por meio de uma conversão de grupo de métodos .(Citando a 5a bala aqui , ênfase minha)
Nesse caso, é convertido para o
test
tipo de delegado.Em outras palavras,
return f
não funciona porquef
já possui um tipo, masf.Invoke
ainda não possui um tipo.fonte
O problema aqui é a compatibilidade de tipos:
A seguir está a definição de delegado do Func de fontes do MSDN:
public delegate TResult Func<in T, out TResult>(T arg);
Se você vir uma relação direta entre o Func mencionado acima e o seu Delegado definido:
public delegate int test(int i);
Os delegados são comparados usando a assinatura, que é os parâmetros de entrada e o resultado da saída; em última análise, um delegado é um ponteiro de função e duas funções podem ser comparadas apenas via assinatura. No tempo de execução, o método chamado via Func é atribuído ao
Test
delegado, uma vez que Signature é o mesmo, ele funciona perfeitamente. É uma atribuição de ponteiro de função, na qualTest
delegado agora chamará o método apontado pelo delegado FuncEntre o Func e o delegado de teste, não há compatibilidade de tipo / atribuição, o Func não pode preencher como parte das regras do sistema Tipo. Mesmo quando seu resultado pode ser atribuído e preenchido
test delegate
como foi feito no primeiro caso.fonte