2010-05-06 23 views
12

Esta es una pregunta derivada que está relacionada con otra que pregunté here. Lo estoy dividiendo porque es realmente una pregunta secundaria:C# 4.0: fundición dinámica a estática

Tengo dificultades para convertir un objeto del tipo dynamic en otro tipo estático (conocido).

que tienen una secuencia de comandos IronPython que está haciendo esto:

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

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

nota que es simplemente Newing un tipo BCL System.Uri y devolviéndolo. Así que Sé el tipo estático del objeto devuelto.

ahora sobre la tierra en C#, estoy Newing el guión alojamiento cosas y llamar a este captador para devolver el objeto Uri:

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

Obras no hay problema. Ahora puedo usar el objeto Uri fuertemente tipado como si originalmente se hubiera instanciado estáticamente.

embargo ....

Ahora quiero definir mi propia clase C# que será renovada dinámica en la tierra al igual que hice con el Uri. Mi sencilla clase C#:

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

Ahora en Python, nuevo un objeto de este tipo y devolverlo:

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

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

después en C# llaman el captador:

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

aquí, el yeso no funciona Tenga en cuenta que la 'prueba' objeto (dinámica) es válida - Puedo llamar a la HacerAlgo() - simplemente no abandonará al tipo estático conocido

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

así que estoy perplejo. el System.Uri de tipo BCL convertirá de un tipo dinámico al estático correcto, pero mi propio tipo no lo hará. Obviamente hay algo que no estoy haciendo esto ...

-

Actualización: Me hice un montón de pruebas para asegurarse de que mis referencias de montaje están alineando correctamente. Cambié el número de ver ensamblador referenciado y luego miré en dynamic Objetos GetType() información en C# - es el número de versión correcto, pero aún no volverá al tipo estático conocido.

Luego creé otra clase en mi aplicación de consola para verificar que obtendría el mismo resultado, que resultó positivo: puedo obtener una referencia dynamic en C# a un tipo estático instanciado en mi secuencia de comandos de Python, pero lo hará no volver al tipo estático conocido correctamente.

-

aún más información:

Anton sugiere a continuación que el dominio de aplicación de montaje contexto de unión es la causa más probable. Después de hacer algunas pruebas, creo que es muy probable. . . ¡pero no puedo resolver cómo resolverlo!No estaba al tanto de los contextos de ensamblaje vinculante, así que gracias a Anton me he educado más sobre la resolución de ensamblaje y los errores sutiles que surgen allí.

Así que miré el proceso de resolución de ensamblaje al colocar un controlador en el evento en C# antes de iniciar el motor de script. Eso me permitió ver el motor pitón puesta en marcha y el tiempo de ejecución empezar a resolver los montajes:

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); 

... y el controlador establece el pType var al tipo que Python está cargando:

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"); 
    } 
} 

Entonces, aunque el tipo utilizado por python es el mismo en C#, estoy pensando (como propuso Anton) que los diferentes contextos vinculantes significan que para el tiempo de ejecución, los dos tipos (el del contexto de carga de enlace) y la 'carga desde el contexto de enlace' son diferentes, por lo que no se puede transmitir al otro.

Así que ahora que tengo controlar la Tipo (junto con su contexto vinculante) cargado por Python, he aquí y he aquí en C# que puedo enviar el objeto dinámico para este tipo estático y funciona:

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

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

Pero, suspiro, esto no soluciona totalmente el problema, porque el var pythonBoundContextObject, mientras que del tipo correcto, aún tiene la mancha del contexto de vinculación de ensamblaje equivocado. Esto significa que no puedo pasar esto a otras partes de mi código porque todavía tenemos bizzare type mismatch donde el espectro invisible del contexto vinculante me detiene.

// 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 

Por lo tanto, la resolución tendrá que ser del lado de Python: hacer que el script se cargue en el contexto de encuadernado del conjunto correcto.

en Python, si hago esto:

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

el tiempo de ejecución no puede resolver mi tipo:

import Entity.TestPy #fails 
+1

intenta imprimir test.GetType() – Andrey

+0

Tener en cuenta las [AssemblyVersion] –

+0

Parece más o menos como si tuvieras diferentes versiones de DLL de tu ensamblaje de satélite utilizado por ironpython y C#. – Lucero

Respuesta

3

Aquí es una respuesta por parte del equipo IronPython que cubre el mismo problema:

C#/IronPython Interop with shared C# Class Library

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

+0

+1. ¡Muchas gracias! haciendo un engine.Runtime.LoadAssembly(typeof(WidgetEntities.Widget).Assembly); funciona! El único problema que tengo con la solución es que para generalizar el uso (es decir, quiero tener un componente de alojamiento reutilizable) significa que tengo que conocer todas las referencias posibles que IronPython podría hacer en C#, lo que básicamente significa que tengo que carga todo Yuck. –

2

apuesto a que IronPython carga su entity.dll en una diferente assembly load context, de modo que usted tiene dos copias cargadas y los tipos en ellas son, por supuesto, diferentes. Es posible que pueda solucionar este problema conectando AppDomain.AssemblyReslove/AppDomain.AssemblyLoad y devolviendo su ensamblado local (typeof (Entity.TestPy).Assembly) cuando IronPython intente cargarlo, pero no le garantizo que esto funcionará.

No experimenta esto con System.Uri porque mscorlib.dll (y tal vez algunos otros ensambles de sistema) es tratado especialmente por el tiempo de ejecución.

Actualización:IronPython FAQ estados que si la asamblea no se ha cargado clr.AddReferenceToFileAssembly.LoadFile usos, que carga en el 'Ni' contexto. Intente acceder a un método desde Entity.TestPy antes de llamar a IronPython para cargar el ensamblaje en el contexto predeterminado Load.

+0

(1) He instanciado la clase Entiteis.TestPy en C# antes de actualizar el scriptEngine ... sin suerte (2) El evento AssemblyResolve solo acierta para 'System' ... nunca se dispara para 'Entity'. Creo que estás en el camino correcto, pero ... –

+0

Prueba con 'AssemblyLoad'. –

+0

intentando hacer un Assembly.Load (... nombre de ensamblado FQ ...) en python. esa línea se ejecutará, pero cuando intento importar Entidades.Test, no puede encontrarla. No parece que Python tenga conocimiento del contexto de vinculación de carga. –

Cuestiones relacionadas