Nie mogę zrozumieć, dlaczego nie można kompilować następującego kodu C #.
Jak widać, mam statyczną metodę ogólną Coś z IEnumerable<T>
parametrem (i T
jest ograniczone jako IA
interfejs), a tego parametru nie można niejawnie przekonwertować na IEnumerable<IA>
.
Jakie jest wyjaśnienie? (Nie szukam obejścia, tylko po to, aby zrozumieć, dlaczego to nie działa).
public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }
public static class Test
{
public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
{
var bar = foo.ToList();
// All those calls are legal
Something2(new List<IA>());
Something2(new List<IB>());
Something2(new List<CIA>());
Something2(new List<CIAD>());
Something2(new List<CIB>());
Something2(new List<CIBD>());
Something2(bar.Cast<IA>());
// This call is illegal
Something2(bar);
return bar;
}
private static void Something2(IEnumerable<IA> foo)
{
}
}
Błąd w Something2(bar)
kolejce:
Argument 1: nie można konwertować z „System.Collections.Generic.List” na „System.Collections.Generic.IEnumerable”
c#
covariance
contravariance
BenLaz
źródło
źródło
T
do typów referencyjnych. Jeśli użyjesz tego warunkuwhere T: class, IA
, powinno działać. Połączona odpowiedź zawiera więcej szczegółów.Something2(foo);
wprost. Chodzenie po okolicy w.ToList()
celu uzyskania aList<T>
(T
jest to parametr typu zadeklarowany przez metodę ogólną) nie jest konieczne, aby to zrozumieć (aList<T>
jestIEnumerable<T>
).Odpowiedzi:
Komunikat o błędzie nie zawiera wystarczających informacji i to moja wina. Przepraszam za to.
Problem, którego doświadczasz, jest konsekwencją faktu, że kowariancja działa tylko na typach referencyjnych.
Prawdopodobnie mówisz teraz „ale
IA
jest typem referencyjnym”. Tak to jest. Ale nie powiedziałeś, żeT
to równa sięIA
. Powiedziałeś, żeT
jest to typ, który implementujeIA
, a typ wartości może implementować interfejs . Dlatego nie wiemy, czy kowariancja zadziała i nie zezwalamy na to.Jeśli chcesz, aby kowariancja działała, musisz powiedzieć kompilatorowi, że parametr typu jest typem referencyjnym z
class
ograniczeniem, a także zIA
ograniczeniem interfejsu.Komunikat o błędzie powinien naprawdę mówić, że konwersja nie jest możliwa, ponieważ kowariancja wymaga gwarancji typu odniesienia, ponieważ jest to podstawowy problem.
źródło
customers.Select(c=>c.FristName)
? Specyfikacja języka C # jest bardzo jasna, że jest to błąd rozpoznawania przeciążenia : zestaw odpowiednich metod o nazwie Select, które mogą przyjąć, że lambda jest pusty. Ale główną przyczyną jestFirstName
literówka.Chciałem tylko uzupełnić doskonałą odpowiedź Erica, podając przykład kodu dla tych, którzy mogą nie być zaznajomieni z ogólnymi ograniczeniami.
Zmień
Something
„s podpis tak:class
ograniczenie musi przyjść pierwszy .źródło
primary_constraint ',' secondary_constraints ',' constructor_constraint
class
jest zły, ponieważ oznacza „typ odniesienia”, a nie „klasę”. Byłbym szczęśliwszy z czymś gadatliwy jakwhere T is not struct