2013-03-11 9 views
6

Mam więc klasę, która akceptuje ogólny parametr typu i wykonuje trochę specjalną obsługę, jeśli parametr typu jest podklasą danego typu.Generowanie rzutowania typu ogólnego w zapytaniu linq

IEnumerable<T> models = ... 

// Special handling of MySpecialModel 
if (filterString != null && typeof(MySpecialModel).IsAssignableFrom(typeof(T))) 
{ 
    var filters = filterString.Split(...); 
    models = 
     from m in models.Cast<MySpecialModel>() 
     where (from t in m.Tags 
       from f in filters 
       where t.IndexOf(f, StringComparison.CurrentCultureIgnoreCase) >= 0 
       select t) 
       .Any() 
     select (T)m; 
} 

Ale ja dostaję wyjątek w ostatnim wierszu

Cannot convert type 'MySpecialModel' to 'T' 

Gdybym zmienić kod do korzystania as zamiast odlewania, otrzymuję ten błąd.

The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint. 

Czego mi tu brakuje?

Aktualizacja

Ta klasa potrzeby może podjąć każdą parametr typu, w tym struct s oraz wbudowanych typów, więc ogólna ograniczenie nie byłoby odpowiednim rozwiązaniem w moim przypadku.

+0

czy ustawiłeś ograniczenie 'where T: class' w swojej klasie ogólnej? –

+0

@danradu Nie, ale to nie zadziałałoby w moim przypadku, ponieważ klasa generyczna może akceptować zarówno parametry referencyjne, jak i typu wartości. –

+0

@ p.s.w.g, patrz aktualizacja – smartcaveman

Odpowiedz

3

uwagi Select(x => (MySpecialModel)x)

Sposób LINQ Cast<T> będzie działać tylko do odlewania elementów z tym, że element jest już (na przykład typu podstawowego, typu pochodnej lub interfejsem). Nie ma na celu rzutowania obiektów, które mogą być rzutowane na typ docelowy. (Np new List<int>{1,2,3}.Cast<long>() rzuci wyjątek, jak również.

Powyższa odpowiedź nie była zła, ale nie ma odpowiedzi na pytanie.

Tylko dlatego, że okazały się refleksji, że parametr związany jest rodzajowy dany typ, nie oznacza, że ​​kompilator wie, że tak jest. Aby to zadziałało, musisz rzucić instancję T do wspólnego typu (np. object), a następnie przesłać go do określonego typu, np. (zmiana ostatniej linii w zapytaniu na select (T)(object)m powinna załatwić sprawę:

+0

Próbowano 'Wybierz (x => (T) x)'. ten sam wynik: –

+0

ohh moje złe, zmień ostatnią linię 'wybierz (T) (obiekt) m') - Nie przeczytałem tego pytania całkowicie przed udzieleniem odpowiedzi – smartcaveman

+0

Dzięki. Double-cast działa, ale wygląda dziwnie. W końcu skończyłem z '.Cast ()' (co jest równoważne i jest bardziej estetyczne). Jeśli znasz jakąkolwiek przyczynę, podwójny rzut okaże się lepszy, proszę daj mi znać. –

1

Aby użyć słowa kluczowego as, należy class ograniczenie na generycznego parametru:

void MyMethod<T>(T item) where T : class 
{ 
    //... 
} 
+0

Nie chcę tego robić, ponieważ ta klasa może być również użyte do 'struct'. –

1

Jeśli wiesz, że typ rodzajowy zawsze będzie klasa, można dodać do kolumny na swojej klasie:

public class Test<T> where T : class {} 

przeciwnym razie należy wykonać podwójne obsady przez obiekt jako smartcaveman zasugerował:

.Select(x => (T)(object)x); 
+0

przeczytaj to jeszcze raz - przejrzałem za pierwszym razem, ale on rzeczywiście pytał, jak rzucić parametr' T' bezpośrednio na 'MySpecialModel', co nie jest możliwe. – smartcaveman

0

można zastosować Nullable<T> ograniczenie - które powinno umożliwić możliwość rzucenia (przynajmniej używając "jak").

1

Najprostszym rozwiązaniem jest oddane do object zanim obsady do T:

select (T)(object)m; 

Problemem jest czek występuje w czasie wykonywania, ale kompilator nie wie, że T musi być instancją MySpecialModel w oświadczeniu if.Dlatego po prostu widzi, że próbujesz rzutować na dowolny dowolny typ T z MySpecialModel, który nie jest bezpieczny, stąd błąd.

2

Wypróbuj następujące

select (T)(object)m; 

Na starcie masz pewność, że T jest podtypem MySpecialModel ale kompilator nie ma dostępu do tych informacji w czasie kompilacji. Po prostu widzi próbę konwersji między 2 niepowiązanymi typami: T i MySpecialModel.

Aby obejść ten problem, należy użyć object jako środkowego mężczyzny. Kompilator rozumie, jak przekonwertować MySpecialModel na object i przejść z object do T.

Powiązane problemy