Czy jest to błąd kompilatora, czy istnieje konkretny powód, dla którego operator warunkowo-null nie działa z Func
wewnątrz ogólnych metod?Operator null-conditional nie działa z Func <T> wewnątrz ogólnej metody
Aby dać przykład następujące nie kompilacji
public static T Test<T>(Func<T> func)
{
return func?.Invoke() ?? default(T);
}
Błąd kompilator produkuje jest CS0023 Operator '?' cannot be applied to operand of type 'T'
Jestem świadomy, że można osiągnąć to samo robi to jednak:
public static T Test<T>(Func<T> func)
{
return func != null ? func() : default(T);
}
Dlaczego więc jest to zabronione?
Aby opracować dalsze Action<T>
działa jednak zgodnie z oczekiwaniami.
public static void Test<T>(Action<T> action, T arg)
{
action?.Invoke(arg);
}
Aktualizacja (17.01.2017):
Po kilku dalszych badań, to sprawia, że nawet mniej sensu, nawet z następującą:
Powiedzmy, że mamy klasę (Odnośnik -type)
public class Foo
{
public int Bar { get; set; }
}
i powiedzmy, że mamy Func<int>
Func<int> fun =() => 10;
następujące utwory:
// 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
które oznacza według logiki zastosowane tam wtedy Func<T>
typu A, o wartości używając null-conditional
i null-coalescing
operatorów powinno działać.
Jednak, gdy tylko lewy generyczny typ null-conditional
jest rodzajowy bez ograniczeń, to nie można zastosować tę samą logikę, że powinien on być w stanie biorąc pod uwagę to może zastosować tę samą logikę do obu typów wartości i typy-odniesienia, gdy typy są jawnie stosowane.
Jestem świadomy ograniczeń kompilatorów, po prostu nie ma dla mnie sensu, dlaczego nie pozwala na to i dlaczego chce, aby wynik był inny, niezależnie od tego, czy jest to referencja czy typ wartości, biorąc pod uwagę ręczne stosowanie typów. przyniesie oczekiwane rezultaty.
'var x = func? .Invoke()' zawiedzie też. 'x' może być puste lub mieć jakąś wartość. kompilator tego nie wie. poza tym kompilator nie wie, czy 'T' jest typem referencyjnym czy nie. zauważ, że 'null' nie jest poprawne dla typów wartości. na przykład nie możesz napisać 'int I = null'. a więc błąd, który otrzymujesz. –
W skrócie, typ 'Func? .Invoke()' musi być 'T', jeśli' T' jest typem odniesienia, oraz 'T?' Jeśli 'T' jest typem wartości. Ponieważ generics w .NET ma jedną implementację (w przeciwieństwie do szablonów w C++), nie można tego zrobić łatwo. Teoretycznie kompilator może pochylić się do tyłu, aby to zadziałało przez sprytne generowanie kodu. W praktyce filozofią kompilatora C# jest nie pochylanie się do tyłu, ale odrzucanie rzeczy, jeśli nie można tego zrobić bezpośrednio. –