Pourquoi la compilation est-elle OK, lorsque j'utilise la méthode Invoke, et pas OK lorsque je retourne Func <int, int> directement?

28

Je ne comprends pas ce cas:

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
}

Pourquoi la compilation est-elle OK lorsque j'utilise la Invokeméthode et pas OK lorsque je reviens csharp Func<int,int>directement?

Evgeniy Terekhin
la source
Vous avez un délégué, ce qui signifie que vous obtenez une sorte d'événement. L'invocation empêche l'exception inter-thread et permet à plusieurs processus d'accéder à l'objet.
jdweng
Notez que vous verrez ce problème même si vous utilisez deux délégués d'apparence identique tels que delegate void test1(int i);etdelegate void test2(int i);
Matthew Watson

Réponses:

27

Il y a deux choses que vous devez savoir pour comprendre ce comportement.

  1. Tous les délégués dérivent de System.Delegate, mais différents délégués ont des types différents et ne peuvent donc pas être affectés les uns aux autres.
  2. Le langage C # fournit une gestion spéciale pour attribuer une méthode ou lambda à un délégué .

Étant donné que différents délégués ont des types différents, cela signifie que vous ne pouvez pas affecter un délégué d'un type à un autre.

Par exemple, étant donné:

delegate void test1(int i);
delegate void test2(int i);

Alors:

test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a;                 // Using normal assignment, therefore does not compile.

La première ligne ci-dessus compile OK car elle utilise la gestion spéciale pour affecter un lambda ou une méthode à un délégué.

En fait, cette ligne est effectivement réécrite comme ceci par le compilateur:

test1 a = new test1(Console.WriteLine);

La deuxième ligne ci-dessus ne se compile pas car elle essaie d'affecter une instance d'un type à un autre type incompatible.

En ce qui concerne les types, il n'y a pas d'affectation compatible entre test1et test2car ce sont des types différents.

Si cela aide à y penser, considérez cette hiérarchie de classes:

class Base
{
}

class Test1 : Base
{
}

class Test2 : Base
{
}

Le code suivant ne compile pas, même si Test1et Test2tirer de la même classe de base:

Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.

Cela explique pourquoi vous ne pouvez pas affecter un type de délégué à un autre. C'est juste le langage C # normal.

Cependant, la chose cruciale est de comprendre pourquoi vous êtes autorisé à affecter une méthode ou lambda à un délégué compatible. Comme indiqué ci-dessus, cela fait partie du support du langage C # pour les délégués.

Alors enfin pour répondre à votre question:

Lorsque vous utilisez, Invoke()vous attribuez un appel METHOD au délégué en utilisant le traitement spécial du langage C # pour affecter des méthodes ou des lambdas à un délégué plutôt que d'essayer d'attribuer un type incompatible - par conséquent, il compile OK.

Pour être tout à fait clair, le code qui se compile dans votre OP:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

Est en fait converti conceptuellement en quelque chose comme:

public test Success()
{
    Func<int, int> f = x => x;
    return new test(f.Invoke);
}

Alors que le code défaillant tente d'affecter entre deux types incompatibles:

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // Attempting to assign one delegate type to another: Fails
}
Matthew Watson
la source
6

Dans le second cas, fest de type Func<int, int>, mais la méthode est censée retourner a test. Ce sont des types non liés (délégués), qui ne sont pas convertibles entre eux, donc une erreur de compilation se produit. Vous pouvez accéder à cette section de la spécification de langue et rechercher «déléguer». Vous ne trouverez aucune mention des conversions entre les délégués qui ont les mêmes signatures.

Dans le premier cas cependant, il f.Invokes'agit d'une expression de groupe de méthodes , qui n'a en fait pas de type. Le compilateur C # convertira les expressions de groupe de méthodes en types de délégués spécifiques en fonction du contexte, via une conversion de groupe de méthodes .

(Citant la 5ème puce ici , c'est moi qui souligne)

Une expression est classée comme l'une des suivantes:

  • ...

  • Un groupe de méthodes, qui est un ensemble de méthodes surchargées résultant d'une recherche de membre. [...] Un groupe de méthodes est autorisé dans une expression_invocation, une expression_création_délégué et comme côté gauche d'un isopérateur, et peut être implicitement converti en un type délégué compatible.

Dans ce cas, il est converti en testtype délégué.

En d'autres termes, return fcela ne fonctionne pas car a fdéjà un type, mais f.Invoken'a pas encore de type.

Balayeuse
la source
2

Le problème ici est la compatibilité de type:

Voici la définition du délégué Func des sources MSDN:

public delegate TResult Func<in T, out TResult>(T arg);

Si vous voyez qu'il n'y a pas de relation directe entre le Func mentionné ci-dessus et votre délégué défini:

public delegate int test(int i);

Pourquoi le premier extrait se compile:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
 }

Les délégués sont comparés à l'aide de la signature, qui est les paramètres d'entrée et le résultat de sortie, finalement un délégué est un pointeur de fonction et deux fonctions peuvent être comparées uniquement via la signature. Au moment de l'exécution, la méthode invoquée via Func est attribuée au Testdélégué, puisque Signature est la même, elle fonctionne de manière transparente. Il s'agit d'une affectation de pointeur de fonction, où le Testdélégué invoquera désormais la méthode pointée par le délégué Func

Pourquoi 2nd Snippet ne parvient pas à compiler

Entre Func et délégué de test, il n'y a pas de compatibilité de type / affectation, Func ne peut pas remplir dans le cadre des règles système Type. Même lorsque son résultat peut être attribué et renseigné test delegatecomme dans le premier cas.

Mrinal Kamboj
la source