Est-ce un bogue de compilateur ou existe-t-il une raison spécifique pour laquelle l'opérateur null-conditionnel ne fonctionne pas avec Func
dans les méthodes génériques?L'opérateur null-conditionnel ne fonctionne pas avec Func <T> dans une méthode générique
Pour donner un exemple ce qui suit ne compile pas
public static T Test<T>(Func<T> func)
{
return func?.Invoke() ?? default(T);
}
L'erreur du compilateur produit est CS0023 Operator '?' cannot be applied to operand of type 'T'
Je suis conscient que vous pouvez obtenir le même faisant cela cependant:
public static T Test<T>(Func<T> func)
{
return func != null ? func() : default(T);
}
Alors, pourquoi est-ce interdit? Pour compléter davantage, Action<T>
fonctionne cependant comme prévu.
public static void Test<T>(Action<T> action, T arg)
{
action?.Invoke(arg);
}
Mise à jour (17/01/2017):
Après quelques recherches, il est encore moins de sens, même avec les éléments suivants:
Disons que nous avons une classe (Référence -type)
public class Foo
{
public int Bar { get; set; }
}
et disons que nous avons un Func<int>
Func<int> fun =() => 10;
Les travaux suivants:
// This work
var nullableBar = foo?.Bar; // type of nullableBar is int?
var bar = nullableBar ?? default(int); // type of bar is int
// And this work
nullableBar = fun?.Invoke(); // ditto
bar = nullableBar ?? default(int); // ditto
Ce qui signifie selon la logique, il applique alors un Func<T>
d'une valeur de type en utilisant null-conditional
et null-coalescing
opérateurs devraient travailler.
Cependant, dès que le type générique gauche du null-conditional
est générique sans contraintes, il ne peut pas appliquer la même logique qu'il devrait pouvoir considérer, il peut appliquer la même logique aux deux types de valeurs et types-références lorsque les types sont explicitement appliqués. Je connais les contraintes des compilateurs, cela n'a pas de sens pour moi pourquoi il ne le permet pas et pourquoi il veut que la sortie soit différente que ce soit une référence ou un type de valeur considérant l'application manuelle des types donnera les résultats attendus.
'var x = func? .invoke()' échouera aussi. 'x' peut être nul ou avoir une certaine valeur. le compilateur ne le sait pas. d'ailleurs ce compilateur ne sait pas si 'T' est le type de référence ou non. notez que 'null' n'est pas valide sur les types de valeur. par exemple vous ne pouvez pas écrire 'int I = null'. donc l'erreur que vous obtenez. –
En un mot, le type 'Func? .Invoke()' doit être 'T' si' T' est un type de référence, et 'T?' Si 'T' est un type de valeur. Puisque les génériques dans .NET ont une implémentation (contrairement aux templates en C++), cela ne peut pas être fait facilement. En théorie, le compilateur pourrait se pencher en arrière pour que cela fonctionne grâce à une génération intelligente de code. En pratique, la philosophie du compilateur C# n'est pas de se pencher en arrière mais de refuser des choses si elles ne peuvent pas être faites directement. –