2013-03-11 19 views
6

Quindi ho una classe che accetta un parametro di tipo generico e fa una piccola gestione speciale se il parametro type è una sottoclasse di un determinato tipo.Trasmissione di tipo generico nella query 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; 
} 

Ma sto un'eccezione sull'ultima riga

Cannot convert type 'MySpecialModel' to 'T' 

Se cambio il codice per utilizzare as invece di casting, ottengo questo errore.

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

Cosa mi manca qui?

Aggiornamento

Questa classe bisogni possono prendere qualsiasi parametro tipo, inclusi struct s e tipi built-in, in modo un vincolo generico non sarebbe una soluzione adeguata nel mio caso.

+0

hai impostato il vincolo 'dove T: class' nella tua classe generica? –

+0

@danradu No, ma non funzionerebbe nel mio caso in quanto la classe generica può accettare sia parametri di riferimento che di tipo valore. –

+0

@ p.s.w.g, vedere aggiornamento – smartcaveman

risposta

3

fare Select(x => (MySpecialModel)x)

Procedimento LINQ Cast<T> funziona solo per elementi per la colatura per che l'elemento è già (ad esempio un tipo di base, tipo derivato, o interfaccia). Non è inteso per lanciare oggetti che possono essere lanciati su un tipo di bersaglio. (Ad esempio new List<int>{1,2,3}.Cast<long>() un'eccezione pure.

La risposta di cui sopra non era sbagliato, ma non affronta la questione.

Solo perché avete dimostrato con la riflessione che un parametro generico è destinata a un determinato tipo, non significa che il compilatore sa che lo è. Per fare in modo che questo funzioni, devi lanciare l'istanza T in un tipo comune (ad esempio object), quindi trasmetterlo al tipo specifico. modificare l'ultima riga della query su select (T)(object)m dovrebbe fare il trucco

+0

Provato 'Selezionare (x => (T) x)'. stesso risultato –

+0

ohh mio male, cambia l'ultima riga 'seleziona (T) (oggetto) m') - Non ho letto la domanda completamente prima di rispondere a – smartcaveman

+0

Grazie. Il doppio cast funziona, ma sembra strano. Alla fine ho finito con il '.Cast ()' (che è equivalente ed è più estetico). Se sai che per qualsiasi motivo il doppio cast sarebbe meglio, per favore fammelo sapere. –

1

Per utilizzare la parola chiave as, inserire il vincolo class sul parametro generico:

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

Non voglio farlo perché questa classe può anche essere usato per 'struct''s. –

1

Se si sa che il tipo generico sarà sempre una classe, è possibile aggiungere un vincolo di tipo sulla vostra classe:

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

In caso contrario eseguire un doppio getto tramite oggetto come smartcaveman ha suggerito:

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

rileggerlo di nuovo - Ho scremato anche la prima volta ma in realtà mi chiedeva come trasmettere il parametro' T' direttamente a 'MySpecialModel', cosa che non è possibile. – smartcaveman

0

si potrebbe applicare Nullable<T> vincolo - che dovrebbe consentire la possibilità di lanciare (almeno usando "come").

1

La correzione più semplice è quello di gettare al object prima il cast a T:

select (T)(object)m; 

Il problema è il vostro assegno si verifica in fase di esecuzione, ma il compilatore non sa che T deve essere un esempio di MySpecialModel all'interno della dichiarazione if.Pertanto, si vede solo che si sta provando a trasmettere su un tipo arbitrario MySpecialModel di tipo arbitrario che non è sicuro, quindi l'errore.

2

Provate il seguente

select (T)(object)m; 

In fase di runtime aver verificato che T è un sottotipo di MySpecialModel ma il compilatore non ha accesso a queste informazioni in fase di compilazione. Verrà appena tentata la conversione tra 2 tipi non correlati: T e MySpecialModel.

Per ovviare a questo è necessario utilizzare object come un intermediario. Il compilatore comprende come convertire MySpecialModel in object e passare da object a T.

Problemi correlati