2016-02-10 10 views
6

Perché non è possibile eseguire il cast di un'istanza di un delegato in un tipo generico T?Impossibile trasmettere delegato a un tipo generico T

consideri un metodo di utilità CreateDelegate che crea un'istanza di T, che è una delegato, cioè un tipo derivato da MulticastDelegate.

T CreateDelegate<T>() {… } 

Purtroppo i farmaci generici non permette a vincolo T ad un tipo derivato da MulticastDelegate dando il seguente errore di compilazione:

Constraint cannot be special class 'System.MulticastDelegate'

Tuttavia, questo metodo di utilità sta controllando che T è compatibile con MulticastDelegate e creazione di un delegato tramite Reflection tramite Delegate::CreateDelegate. Ma, se cerchiamo di lanciare il risultato di Delegate::CreateDelegate a T, avremo il seguente errore di compilazione:

Cannot convert type 'System.Delegate' to 'T'

Tuttavia, se io scaccio prima a object e poi a T funzionerà bene:

T h = (T) ((object) Delegate.CreateDelegate(typeof(T), target, m)); 

Perché non possiamo inoltrare direttamente il delegato a T?

+0

funziona così: '' T h = Delegate.CreateDelegate (typeof (T), target, m) come T; ''? –

+0

Sì, funziona. E, ha ancora meno senso. IMHO il comportamento di entrambi gli operatori ('as' e _cast_) dovrebbe essere coerente. –

+0

in questo modo è più sicuro che lanciare l'oggetto quindi da oggetto a T, ti darà null se cast fallisce –

risposta

10

Il linguaggio C# impone un controllo statico se un cast da tipo X a tipo Y è valido, ovvero se produce senso in quanto il compilatore può (in certa misura) garantire la compatibilità e rifiutare gli errori che risultano chiari in compilazione -tempo. Un tipo generico non vincolato T e System.Delegate non ha nulla in comune. Tuttavia, quando viene lanciato su object, il compilatore sa che ogni tipo è essenzialmente un object, quindi consente il cast. Ciò non significa che un controllo di tipo run-time non fallirà in un caso particolare.

L'operatore as è un po 'più permissivo, in quanto non causerà un'eccezione per un cast altrimenti non valido. Il compilatore è anche meno severo nell'applicare controlli statici. Nel tuo caso particolare questo è utile in quanto puoi omettere il cast intermedio su object e utilizzare as T. Tuttavia, è necessario che as funzioni solo sui tipi di classe, quindi è necessario applicare il vincolo where T : class.

Quindi il metodo allora sarebbe simile a questa (semplificato):

public T CreateDelegate<T>(…) where T : class 
{ 
    return Delegate.CreateDelegate(typeof(T), …) as T; 
} 

Come @usr suggerito, una lettura consigliata è il blog di Eric Lippert, per esempio this article on casts and type parameters.