2016-05-23 48 views
14

mi chiedo se qualcuno potrebbe spiegare il motivo per cui in questo codicePerché un parametro dinamico in un metodo generico genera un'eccezione di riferimento null quando si utilizza un oggetto?

public class SomeClass 
{ 
    public T GenericMethod<T>(dynamic value) 
    { 
     return (T)value; 
    } 
} 

il 'valore di ritorno;' dichiarazione genera un'eccezione riferimento null quando viene chiamato con:

new SomeClass().GenericMethod<object>(new object()); // throws System.NullReferenceException 

Esso funziona come previsto quando viene chiamato con:

new SomeClass().GenericMethod<string>("SomeString"); // returns SomeString 
new SomeClass().GenericMethod<object>("SomeString"); // returns SomeString 

Nota: I seguenti compila e funziona bene

public class SomeOtherClass 
{ 
    public T GenericMethod<T>(object value) 
    { 
     return (T)value; 
    } 
} 

The stacktrace:

System.NullReferenceException: Object reference not set to an instance of an object. 
    at Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.GenerateLambda(EXPRCALL pExpr) 
    at Microsoft.CSharp.RuntimeBinder.Semantics.ExprVisitorBase.Visit(EXPR pExpr) 
    at Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.Rewrite(TypeManager typeManager, EXPR pExpr, IEnumerable`1 listOfParameters) 
    at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.BindCore(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] args, DynamicMetaObject& deferredBinding) 
    at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.Bind(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] args, DynamicMetaObject& deferredBinding) 
    at Microsoft.CSharp.RuntimeBinder.BinderHelper.Bind(DynamicMetaObjectBinder action, RuntimeBinder binder, IEnumerable`1 args, IEnumerable`1 arginfos, DynamicMetaObject onBindingError) 
    at Microsoft.CSharp.RuntimeBinder.CSharpConvertBinder.FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion) 
    at System.Dynamic.DynamicMetaObject.BindConvert(ConvertBinder binder) 
    at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel) 
    at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args) 
    at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0) 
+0

Nizza trovare! Speriamo che qualche esperto di compilatori abbia una spiegazione. –

+1

Sospetto che abbia qualcosa a che fare con il cast dinamico su 'T' (anche se il tipo effettivo è lo stesso). Se fai 'restituire il valore come T' funziona pure. –

+0

La parola chiave dynamic indica al compilatore di non applicare regole aggiuntive sul codice, motivo per cui penso che sia consentito compilare. Per quanto riguarda il tipo di ritorno effettivo, sembra che l'argomento di input del valore contenga un valore e sembra come se fosse possibile eseguire il cast del valore ('value as T', ad esempio) –

risposta

2

Sembra it's a bug la prima volta nel 2012, ma a quanto pare non ancora fissato (al 24 maggio 2016)

+0

Great Find - Ho contrassegnato questo come duplicato di quello. –

+0

Effettivamente una bella scoperta! Supponiamo che questa sia l'unica risposta giusta per questa domanda – pvill

1

Aggiornamento

Come il marked duplicate indica questo è un bug noto nel runtime .NET.


Il problema è probabilmente una scarsa indicazione di una trasmissione non valida di runtime. Se si prende il binding dinamico e fare il tipo di parametro object si ottiene l'errore del compilatore

Impossibile convertire implicitamente il tipo 'oggetto' a 'T'. Una conversione esplicita esiste (che le manca un cast?)

Dal aggiungendo dynamic questo casting è fatto a run-time, questo errore conversione implicita si manifesta in modo diverso a run-time con un vago NullReferenceException nel run- legature di tempo.

Io non sono un esperto di funzionamento interno del DLR, ma ho il sospetto che un oggetto passato come valore dynamic non è davvero un puro object a run-time. Sospetto che si tratti di una sorta di wrapper attorno allo object e che quindi non possa essere trasmesso implicitamente all'oggetto in fase di esecuzione.

Un riferimento esplicito gettato

return (T)(object)value; 

non genererà tale errore.

+0

Questo è il valore corretto 'return (T) (object);' rimuove l'errore, ma quale è il motivo per cui il valore restituito (T) non lo è. Perché è necessario il cast aggiuntivo a (oggetto) – pvill

+0

@pvill perché in alcuni casi lo stesso operatore di cast viene utilizzato anche per le conversioni (ad esempio 'int i = 1; long l = (long) l;'). È trattato nella sezione 6.2.7 della specifica C#. L'aggiunta del cast all'oggetto specifica che l'operazione è un vero cast di riferimento e non una conversione implicita. –

+0

Questo tipo di conversione specificato in quella particolare sezione non dovrebbe essere necessario qui. 'new SomeClass(). GenericMethod (10);' funziona perfettamente senza la modifica. E 'value' è di tipo object e' T' è anche Object. Non ci dovrebbero essere conversioni ... – pvill