2010-05-06 4 views
12

Questa è una domanda di derivazione correlata a un'altra che ho chiesto a here. Lo sto suddividendo perché è in realtà una sotto-domanda:C# 4.0: trasmissione dinamica a statica

Ho difficoltà a trasmettere un oggetto di tipo dynamic a un altro (noto) tipo statico.

devo uno script IronPython che sta facendo questo:

import clr 
clr.AddReference("System") 
from System import * 

def GetBclUri(): 
    return Uri("http://google.com") 

nota che è semplicemente Newing un tipo BCL System.Uri e il ritorno. So Conosco il tipo statico dell'oggetto restituito.

ora più in C# paese, io sono Newing lo script di hosting roba e chiamare questa getter per restituire l'oggetto Uri:

dynamic uri = scriptEngine.GetBclUri(); 
System.Uri u = uri as System.Uri; // casts the dynamic to static fine 

Opere nessun problema. Ora posso usare l'oggetto Uri fortemente tipizzato come se fosse stato istanziato in modo statico in modo statico.

però ....

Ora voglio definire la mia classe C# che sarà newed in dinamico paese, proprio come ho fatto con l'Uri. Il mio semplice C# classe:

namespace Entity 
{ 
    public class TestPy // stupid simple test class of my own 
    { 
     public string DoSomething(string something) 
     { 
      return something; 
     } 
    } 
} 

Ora in Python, nuovo un oggetto di questo tipo e restituirla:

sys.path.append(r'C:..path here...') 
clr.AddReferenceToFile("entity.dll") 
import Entity.TestPy 

def GetTest(): 
    return Entity.TestPy(); // the C# class 

poi in C# chiamano il getter:

dynamic test = scriptEngine.GetTest(); 
Entity.TestPy t = test as Entity.TestPy; // t==null!!! 

qui, il cast non funziona. Si noti che l'oggetto 'test' (dinamica) è valida - posso chiamare il DoSomething() - semplicemente non sarà gettato al tipo statico noto

string s = test.DoSomething("asdf"); // dynamic object works fine 

quindi sono perplesso. il tipo BCL System.Uri eseguirà il cast da un tipo dinamico a quello statico corretto, ma il mio tipo non lo farà. C'è ovviamente qualcosa non sto ottenendo su questo ...

-

Aggiornamento: Ho fatto un sacco di test per assicurarsi che i miei arbitri di assemblaggio sono tutti in fila in modo corretto. Ho cambiato il numero ver dell'assembly di riferimento, quindi ho esaminato le informazioni GetType() di dynamic oggetti in C#: è il numero di versione corretto, ma non verrà eseguito il cast del tipo statico conosciuto.

Ho quindi creato un'altra classe nella mia app per console per verificare che otterrei lo stesso risultato, che è risultato positivo: posso ottenere un riferimento dynamic in C# a un tipo statico istanziato nel mio script Python, ma sarà non ricondurre correttamente al tipo statico conosciuto.

-

ancora più informazioni:

Anton suggerisce di seguito che il dominio di applicazione di montaggio contesto vincolante è il probabile colpevole. Dopo aver fatto alcuni test penso che sia molto probabile. . . ma non riesco a capire come risolverlo!Non ero a conoscenza dei contesti di assemblaggio, quindi grazie ad Anton sono diventato più istruito sulla risoluzione dell'assemblaggio e sugli insidiosi bug che emergono.

Così ho visto il processo di risoluzione dell'assieme inserendo un gestore sull'evento in C# prima di avviare il motore di script. Questo mi ha permesso di vedere il motore pitone avvio e il runtime iniziare a risolvere le assemblee:

private static Type pType = null; // this will be the python type ref 

// prior to script engine starting, start monitoring assembly resolution 
AppDomain.CurrentDomain.AssemblyResolve 
      += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 

... e il gestore imposta la var PTYPE al tipo che Python sta caricando:

static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) 
{ 

    if (args.LoadedAssembly.FullName == 
     "Entity, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null") 
    { 
     // when the script engine loads the entity assembly, get a reference 
     // to that type so we can use it to cast to later. 
     // This Type ref magically carries with it (invisibly as far as I can 
     // tell) the assembly binding context 
     pType = args.LoadedAssembly.GetType("Entity.TestPy"); 
    } 
} 

Così mentre il tipo usato da python è lo stesso in C#, sto pensando (come proposto da Anton) che i diversi contesti di associazione significano che al runtime, i due tipi (quello nel "contesto di binding del carico" e il "loadfrom contesto vincolante" sono diversi, quindi non puoi trasmettere l'altro.

Quindi, ora che ho in mano il tipo (insieme con il suo legame contesto) caricato da Python, lo ed ecco in C# posso cast dell'oggetto dinamica a questo tipo statico e funziona:

dynamic test = scriptEngine.GetTest(); 
var pythonBoundContextObject = 
     Convert.ChangeType(test, pType); // pType = python bound 

string wow = pythonBoundContextObject .DoSomething("success"); 

Ma, sospiro, questo non risolve completamente il problema, perché il var pythonBoundContextObject mentre del tipo corretto, continua a contenere la contaminazione del contesto di binding dell'assembly errato. Ciò significa che non posso passare questo ad altre parti del mio codice perché abbiamo ancora questa discrepanza di tipo bizzarro in cui lo spettro invisibile del contesto vincolante mi blocca.

// class that takes type TestPy in the ctor... 
public class Foo 
{ 
    TestPy tp; 

    public Foo(TestPy t) 
    { 
     this.tp = t; 
    } 
} 

// can't pass the pythonBoundContextObject (from above): wrong binding context 
Foo f = new Foo(pythonBoundContextObject); // all aboard the fail boat 

Quindi la risoluzione sta andando ad avere per essere sul lato Python: ricevendo lo script per caricare nel contesto vincolante di montaggio a destra.

in Python, se faccio questo:

# in my python script 
AppDomain.CurrentDomain.Load(
    "Entity, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null"); 

il runtime non può risolvere il mio tipo:

import Entity.TestPy #fails 
+1

tenta di stampare test.GetType() – Andrey

+0

Prestare attenzione alla [AssemblyVersion] –

+0

Sembra quasi che tu stia usando diverse versioni DLL dell'assembly satellite usato da ironpython e C#. – Lucero

risposta

3

Ecco una risposta da parte del team IronPython che copre lo stesso problema:

C#/IronPython Interop with shared C# Class Library

(sollevato da http://lists.ironpython.com/pipermail/users-ironpython.com/2010-September/013717.html)

+0

+1. Molte grazie! facendo un engine.Runtime.LoadAssembly(typeof(WidgetEntities.Widget).Assembly); funziona! L'unico problema che ho con la soluzione è che generalizzare l'uso (cioè voglio essere in grado di avere un componente di hosting riutilizzabile) significa che devo conoscere tutte le possibili referenze che IronPython potrebbe avere in C# che in pratica significa che devo caricare tutto. Che schifo. –

2

scommetto che IronPython carica il entity.dll in un diverso assembly load context, in modo da avere due copie di esso caricate e i tipi in esse sono ovviamente diversi. Potresti essere in grado di aggirare questo problema agganciando AppDomain.AssemblyReslove/AppDomain.AssemblyLoad e restituendo il tuo assembly locale (typeof (Entity.TestPy).Assembly) quando IronPython tenta di caricarlo, ma non garantisco che funzioni.

Non si verifica questo con System.Uri perché mscorlib.dll (e forse alcuni altri gruppi di sistema) è trattato appositamente dal runtime.

Aggiornamento:IronPython FAQ afferma che se il gruppo non è già caricato clr.AddReferenceToFile usa le Assembly.LoadFile, che carica in 'Nessuno' contesto. Prova ad accedere a un metodo da Entity.TestPy prima di chiamare IronPython per caricare l'assembly nel contesto predefinito Load.

+0

(1) Ho istanziato la classe Entiteis.TestPy in C# prima di iniziare lo scriptEngine ... no luck (2) L'evento AssemblyResolve colpisce solo per 'System' ... non si attiva mai per 'Entity'. Penso che tu sia sulla strada giusta, ma ... –

+0

Prova 'AssemblyLoad'. –

+0

tentativo di fare un Assembly.Load (... nome assembly FQ ...) in python. quella linea verrà eseguita, ma quando provo a importare Entities.Test non può trovarlo. Non sembra che Python sia a conoscenza del contesto di caricamento del carico. –