2010-05-06 11 views
12

Il s'agit d'une question secondaire liée à une autre que j'ai posée here. Je le scinde parce que c'est vraiment une sous-question:C# 4.0: transtypage dynamique en statique

J'ai du mal à lancer un objet de type dynamic vers un autre type statique (connu).

J'ai un script IronPython qui fait ceci:

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

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

Notez qu'il est tout simplement newing un type BCL System.Uri et en le retournant. Donc Je connais le type statique de l'objet retourné.

maintenant sur les terres C#, je suis substance d'hébergement newing le script et appeler ce getter pour renvoyer l'objet Uri:

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

Works pas de problème. Je peux maintenant utiliser l'objet Uri fortement typé comme s'il était instancié à l'origine statiquement.

mais ....

Maintenant, je veux définir ma propre classe C# qui sera newed en terre dynamique comme je l'ai fait avec l'Uri. Ma classe simple C#:

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

Maintenant en Python, nouveau un objet de ce type et le retourner:

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

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

alors en C# appellent le getter:

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

ici, la distribution ne fonctionne pas. Notez que l'objet est valide « test » (dynamique) - Je peux appeler le DoSomething() - il ne veut pas jeter le type statique connu

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

donc je suis perplexe. le type BCL System.Uri va passer d'un type dynamique à un type statique correct, mais pas mon propre type. Il y a évidemment quelque chose que je ne reçois pas de cette ...

-

Mise à jour: Je l'ai fait un tas de tests pour vous assurer que mes refs d'assemblage sont toutes correctement la queue. J'ai changé le numéro de ver de l'assembly référencé puis j'ai regardé les objets dynamic GetType() info dans C# - c'est le numéro de version correct, mais il ne sera toujours pas renvoyé au type statique connu. J'ai ensuite créé une autre classe dans mon application de console pour vérifier que j'obtiendrais le même résultat, ce qui s'est avéré positif: je peux obtenir une référence dynamic en C# à un type statique instancié dans mon script Python, mais il faudra pas renvoyé correctement au type statique connu.

-

encore plus d'info:

Anton ci-dessous suggère que le contexte de liaison AppDomain assemblée est le coupable probable. Après avoir fait quelques tests, je pense que c'est très probable. . . mais je ne peux pas comprendre comment le résoudre!Je ne connaissais pas les contextes de liaison d'assemblage, donc grâce à Anton, je suis devenu plus éduqué sur la résolution de l'assemblage et les bugs subtils qui surgissent là-bas. J'ai donc regardé le processus de résolution de l'assemblage en plaçant un gestionnaire sur l'événement en C# avant de démarrer le moteur de script. Cela m'a permis de voir le moteur python démarrage et le moteur d'exécution commencent à résoudre: les ensembles

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

... et le gestionnaire définit le var pType sur le type python chargement:

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

Alors que le type utilisé par python est le même en C#, je pense (comme proposé par Anton) que les différents contextes de liaison signifient que pour l'exécution, les deux types (celui du 'contexte de liaison de chargement' et le 'loadfrom binding context') sont différents - vous ne pouvez donc pas passer à l'autre.

Alors maintenant que j'ai prise du type (avec son contexte de liaison) chargé par Python, voici et voici en C# Je peux jeter l'objet dynamique à ce type statique et il fonctionne:

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

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

Mais, soupir, cela ne résout pas totalement le problème, car le var pythonBoundContextObject du type correct, porte encore le mauvais goût du contexte de liaison d'assemblage. Cela signifie que je ne peux pas passer cela à d'autres parties de mon code parce que nous avons toujours ce désordre de type bizzare où le spectre invisible du contexte de liaison m'arrête froid. Donc, la résolution va devoir être du côté de Python: obtenir le script à charger dans le bon contexte de liaison d'assemblage.

en Python, si je fais ceci:

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

le moteur d'exécution ne peut pas résoudre mon type:

import Entity.TestPy #fails 
+1

essayer d'imprimer test.GetType() – Andrey

+0

attention à [AssemblyVersion] –

+0

Cela ressemble beaucoup à si vous utilisiez des versions DLL différentes de votre assembly satellite utilisées par ironpython et C#. – Lucero

Répondre

3

est ici une réponse de l'équipe IronPython qui couvre le même problème:

C#/IronPython Interop with shared C# Class Library

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

+0

+1. Merci beaucoup! faire un engine.Runtime.LoadAssembly(typeof(WidgetEntities.Widget).Assembly); fonctionne! Le seul problème que j'ai avec la solution est que généraliser l'utilisation (ie je veux être capable d'avoir un composant d'hébergement réutilisable) signifie que je dois connaître toutes les références possibles d'IronPython en C# ce qui signifie que je dois charger tout. Yuck. –

2

Je parie que IronPython charge votre entity.dll dans un assembly load context différent, de sorte que vous avoir deux copies de celui-ci chargé et les types en eux sont bien sûr différents. Vous pouvez peut-être contourner ce problème en accrochant AppDomain.AssemblyReslove/AppDomain.AssemblyLoad et en renvoyant votre assembly local (typeof (Entity.TestPy).Assembly) lorsque IronPython essaie de le charger, mais je ne garantis pas que cela fonctionnera.

Vous ne rencontrez pas cela avec System.Uri car mscorlib.dll (et peut-être d'autres assemblys système) est traité spécialement par le moteur d'exécution.

Mise à jour:IronPython FAQ stipule que si l'ensemble est pas déjà chargé clr.AddReferenceToFile utilise Assembly.LoadFile, qui charge dans le contexte « Ni ». Essayez d'accéder à une méthode à partir de Entity.TestPy avant d'appeler IronPython pour charger l'assembly dans le contexte Load par défaut.

+0

(1) J'ai instancié la classe Entiteis.TestPy en C# avant de lancer le scriptEngine ... pas de chance (2) L'événement AssemblyResolve ne frappe que pour 'System' ... il ne se déclenche jamais pour 'Entity'. Je pense que vous êtes sur la bonne voie, mais ... –

+0

Essayez 'AssemblyLoad'. –

+0

en essayant de faire un Assembly.Load (... nom de l'assembly FQ ...) en python. cette ligne va s'exécuter, mais quand je tente d'importer ensuite Entities.Test il ne peut pas le trouver. Il ne semble pas que Python soit conscient du contexte de la liaison de chargement –