2012-05-25 15 views
5

Mi sono imbattuto in questo caso strano ieri, dove t as D restituisce un valore non nullo, ma (D)t causa un errore del compilatore.Perché questo cast non è valido quando `x as Y` funziona correttamente?

Da quando ero in fretta ho appena usato t as D e portato avanti, ma sono curioso di sapere il motivo per cui il cast non è valido, come t è davvero un D. Qualcuno può far luce sul motivo per cui al compilatore non piace il cast?

class Program 
{ 
    public class B<T> where T : B<T> { } 

    public class D : B<D> { public void M() { Console.Out.WriteLine("D.M called."); } } 

    static void Main() { M(new D()); } 

    public static void M<T>(T t) where T : B<T> 
    { 
     // Works as expected: prints "D.M called." 
     var d = t as D; 
     if (d != null) 
      d.M(); 

     // Compiler error: "Cannot cast expression of type 'T' to type 'D'." 
     // even though t really is a D! 
     if (t is D) 
      ((D)t).M(); 
    } 
} 

EDIT: Giocare in giro, credo che questo è un esempio chiaro. In entrambi i casi, t è limitato a B ed è forse un D. Ma il caso con il generico non verrà compilato. Il C# ignora semplicemente il vincolo generico per determinare se il cast è legale? Anche se lo ignora, t potrebbe ancora essere un D; quindi, perché questo è un errore di tempo di compilazione invece di un'eccezione di runtime?

class Program2 
{ 
    public class B { } 

    public class D : B { public void M() { } } 

    static void Main() 
    { 
     M(new D()); 
    } 

    public static void M(B t) 
    { 
     // Works fine! 
     if (t is D) 
      ((D)t).M(); 
    } 

    public static void M<T>(T t) where T : B 
    { 
     // Compile error! 
     if (t is D) 
      ((D)t).M(); 
    } 
} 
+2

Bet '(D) (oggetto) t' works –

+0

possibile duplicato di [Valore di tipo 'T' non può essere convertito in] ​​(http://stackoverflow.com/questions/4092393/valore-di-tipo-t-cannot-be-converted- a) –

+1

Da un commento in [questo collegamento] (http://stackoverflow.com/questions/1613314/generic-type-casting-method-net) Ho trovato un collegamento a [risposta] (http: // bl ogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx). Essenzialmente, ci sono alcuni tipi di variabili che non possono essere castati ad altri tipi (più specificamente ci sono delle regole sul casting dei tipi di box). Dato che il compilatore non ha idea di cosa T sia in fase di compilazione, deve giocare sul sicuro e negare il cast. –

risposta

3

Nel suo secondo esempio è possibile modificare

((D)t).M(); 

a

((D)((B)t)).M(); 

È possibile lanciare B-D, ma non si può necessariamente lanciare da "qualcosa che è un B" per D. "Qualcosa che è un B" potrebbe essere un A, ad esempio, se A : B.

L'errore del compilatore si verifica quando si sta saltando potenzialmente da un bambino all'altro nella gerarchia. Puoi lanciare su e giù la gerarchia, ma non puoi attraversarla.

((D)t).M();  // potentially across, if t is an A 
((D)((B)t)).M(); // first up the hierarchy, then back down 

Si noti inoltre che si potrebbe comunque ottenere un errore di runtime con ((D)((B)t)).M(); se t non è in realtà un D. (Il vostro check is dovrebbe impedire questo però.)

(anche notare che in nessuno dei due casi è il compilatore tenendo conto del controllo if (t is D).)

Un ultimo esempio:

class Base { } 
class A : Base { } 
class C : Base { } 

... 
A a = new A(); 
C c1 = (C)a;   // compiler error 
C c2 = (C)((Base)a); // no compiler error, but a runtime error (and a resharper warning) 

// the same is true for 'as' 
C c3 = a as C;   // compiler error 
C c4 = (a as Base) as C; // no compiler error, but always evaluates to null (and a resharper warning) 
+0

Quindi, perché 'as' compile? Qual è la differenza tra cast e 'as' in questo caso (_beside_ il suo comportamento in fase di esecuzione)? –

+0

'as' dà lo stesso errore nella stessa situazione. EDIT: in realtà, nell'esempio OP non lo fa. Dovrà giocarci ancora un po '. –

+0

'as' è un animale completamente diverso. Vedere [questo collegamento msdn] (http://msdn.microsoft.com/en-us/library/cscsdfbt%28v=vs.80%29.aspx) - equivale a 'expression is type? (type) espressione: (type) null' –

1

Change a

public static void M<T>(T t) where T : D 

Questa è la limitazione appropriata se si desidera mandato che T deve essere di tipo D.

Non verrà compilato se la restrizione definisce T come D T.

Es: non è possibile lanciare List<int> to int o viceversa

+0

Non chiede come risolvere il problema ma perché viene rifiutato dal compilatore. – Stilgar

+0

Risposta aggiornata. I cast di – TGH

0

La funzione modello ha un vincolo che richiede T per essere B<T>.

Quindi, quando il compilatore tenta di convertire l'oggetto t di tipo T in D non può farlo. Perché T è garantito per essere B<T>, ma non D.

Se si aggiunge un vincolo per richiedere che T sia D, funzionerà. Ad esempio, where T: B<T>, D o semplicemente where T: D che garantisce anche che T è B<T>, a causa della catena di ereditarietà.

E la seconda parte della domanda: quando si chiama t as D viene controllato in fase di esecuzione. E, in fase di runtime, utilizzando il polimorfismo, è stato verificato che t può essere convertito in tipo D ed è eseguito senza errori.

NOTA: a proposito, questo codice è tremendamente strano. Sei sicuro di quello che stai facendo?

+1

vengono controllati anche in fase di esecuzione, in molti casi –

Problemi correlati