2016-06-30 21 views
17

Ho trovato un bug (funzione?) Durante l'apprendimento dinamico in C#. Qualcuno può spiegarmi, perché ho un'eccezione ??Comportamento non prevedibile in C# dynamic

static class Program 
{ 
    public static void Main(string[] args) 
    { 
     dynamic someObj = ConstructSomeObj((Action)(() => Console.WriteLine("wtf"))); 

     var executer = someObj.Execute; 
     executer();   // shows "wtf" 
     someObj.Execute(); // throws RuntimeBinderException 

     Console.ReadKey(); 
    } 

    static dynamic ConstructSomeObj(dynamic param) 
     => new { Execute = param }; 
} 

Nota: typeof sia exectuer e someObj è dinamico

+0

Qual è l'eccezione? Ed è tempo di compilazione o tempo di esecuzione? – RBarryYoung

+1

Sembra che 'dynamic' generi codice per invocare il metodo quando vede' obj.foo() 'e genera il codice per accedere al campo quando vede' obj.foo'. Questo potrebbe essere un bug o almeno un comportamento inaspettato – csharpfolk

+1

Probabilmente le informazioni sul tipo del metodo Execute su someObj non esiste in runtime, dal momento che lambda ha una parte di tipo anonimo e ha senso il contenuto di un tipo anon non pubblico. Vedi un problema simile su tipi dinamici anonimi: http://www.heartysoft.com/ashic/blog/2010/5/anonymous-types-c-sharp-4-dynamic –

risposta

11

Let al seguente codice:

using System; 
using System.Collections.Generic; 


public class Program 
{ 
    public static void Main(string[] args) 
    { 
     Console.WriteLine("first"); 

     // works perfectly!!! 
     dynamic foo = new { x=(Action)(() => Console.WriteLine("ok")) }; 
     foo.x(); 

     // fails 
     dynamic foo2 = new { x=(object)(Action)(() => Console.WriteLine("ok2")) }; 
     foo2.x(); 

    } 
} 

dynamic utilizza la reflection per accedere metodo di oggetti e campi e dal momento che non può conoscere i tipi esatti si deve fare affidamento su informazioni di tipo presente in oggetti su cui opera.

Quando il campo x nel tipo anonimo è digitato correttamente come chiamata del delegato foo.x() funziona perché dinamico può vedere che il valore del campo è delegato.

Quando si utilizza

static dynamic ConstructSomeObj(dynamic param) 
    { return new { x = param }; } 

per creare classe anonima si è creato classe con il campo x di tipo object (dinamica è object dietro le quinte). Quando chiami dinamici obj.x, quel tipo di campo è un object e non si preoccupa di controllare a quale tipo esatto questo campo punta. E poiché l'oggetto non ha il metodo Invoke() come delegato, genera un'eccezione. Se si modifica il tipo di parametro del metodo su Action, funzionerà.

Suppongo che questa decisione di controllare il tipo di campo anziché il tipo di valore contenuto nel campo sia stata presa per fornire prestazioni migliori. In altre parole, quando si controlla la classe di tipo CallSite generata da dynamic, è possibile memorizzarla nella cache e riutilizzarla in seguito.

Riferimenti: https://github.com/mono/mono/blob/ef407901f8fdd9ed8c377dbec8123b5afb932ebb/mcs/class/Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/Binder.cs

https://github.com/mono/mono/blob/ef407901f8fdd9ed8c377dbec8123b5afb932ebb/mcs/class/Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpInvokeMemberBinder.cs

EDIT: Controllato questo su mono, qualcuno può verificare su VS

+0

Non sei sicuro di quale controllo volevi ma in esecuzione su Windows il tuo programma fa esattamente come hai descritto (stampa prima e ok e poi lancia un'eccezione). – Chris

+0

@Chris grazie, purtroppo.NETFiddle utilizza un compilatore obsoleto e attualmente ho accesso solo alle finestre di Linux – csharpfolk

2

ok, interessante. Questo 2 Linee avrebbe funzionato:

Task.Run(someObj.Execute); 
((Action)someObj.Execute)(); 

Sembra che il compilatore non accetta i tipi dinamici() per sempre e in fase di esecuzione il CLR guarda solo 'un livello profondo'. Quindi puoi aiutare qui aggiungendo il cast esplicito o eseguendo il cast implicito con Task.Run(). Se questa è una caratteristica o un bug !? ... nessuna idea ;-) ... aspetto

+0

. Invoke() funziona anche, ma suppongo che sia compilato come il tuo esempio – Martheen