12

这是一个分支问题,与我在这里问的另一个问题有关。我把它分开是因为它真的是一个子问题:

我很难将一个类型的对象转换dynamic为另一个(已知的)静态类型。

我有一个执行此操作的 ironPython 脚本:

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

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

请注意,它只是新建一个 BCL System.Uri 类型并返回它。所以我知道返回对象的静态类型

现在在 C# 领域,我正在更新托管内容的脚本并调用这个 getter 来返回 Uri 对象:

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

工作没问题。我现在可以使用强类型 Uri 对象,就好像它最初是静态实例化的一样。

然而....

现在我想定义我自己的 C# 类,就像我对 Uri 所做的那样,它将在动态领域中更新。我的简单 C# 类:

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

现在在 Python 中,新建一个这种类型的对象并返回它:

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

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

然后在 C# 中调用 getter:

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

在这里,演员表不起作用。请注意,“测试”对象(动态)是有效的——我可以调用 DoSomething()——它只是不会转换为已知的静态类型

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

所以我很困惑。BCL 类型 System.Uri 将从动态类型转换为正确的静态类型,但我自己的类型不会。显然有一些我不明白的事情......

--

更新:我做了一堆测试以确保我的程序集引用都正确排列。我更改了引用的程序集版本号,然后查看了dynamicC# 中的对象 GetType() 信息——它是正确的版本号,但它仍然不会转换回已知的静态类型。

然后我在我的控制台应用程序中创建了另一个类来检查我是否会得到相同的结果,结果是肯定的:我可以dynamic在 C# 中获得对在我的 Python 脚本中实例化的静态类型的引用,但它不会转换回正确知道静态类型。

--

更多信息:

Anton 在下面建议 AppDomain 程序集绑定上下文可能是罪魁祸首。在做了一些测试后,我认为它很可能是。. . 但我不知道如何解决它!我不知道程序集绑定上下文,因此多亏了 Anton,我对程序集解析和那里出现的细微错误有了更多的了解。

所以我通过在启动脚本引擎之前在 C# 中的事件上放置一个处理程序来观察程序集解析过程。这让我可以看到 python 引擎启动并且运行时开始解析程序集:

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

...并且处理程序将 var pType 设置为python 正在加载的类型

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

因此,虽然 python 使用的类型在 C# 中是相同的,但我认为(如 Anton 所提议的)不同的绑定上下文对运行时意味着,这两种类型(“加载绑定上下文”中的一种和'loadfrom binding context) 是不同的——所以你不能转换到另一个。

所以现在我已经掌握了由 Python 加载的类型(以及它的绑定上下文),你瞧,在 C# 中,我可以将动态对象转换为这个静态类型并且它可以工作:

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

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

但是,叹息,这并不能完全解决问题,因为pythonBoundContextObject正确类型的 var仍然带有错误的程序集绑定上下文的污点。这意味着我不能将它传递给我的代码的其他部分,因为我们仍然有这种奇怪的类型不匹配,其中绑定上下文的不可见幽灵让我感到寒冷。

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

因此,解决方案必须在 Python 方面:让脚本加载到正确的程序集绑定上下文中。

在 Python 中,如果我这样做:

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

运行时无法解析我的类型:

import Entity.TestPy #fails
4

2 回答 2

3

这是 IronPython 团队的回答,涵盖了同样的问题:

C# / IronPython 与共享 C# 类库的互操作

(来自http://lists.ironpython.com/pipermail/users-ironpython.com/2010-September/013717.html

于 2010-09-16T07:25:14.737 回答
2

我敢打赌 IronPython 会将您加载entity.dll到不同的程序集加载上下文中,因此您加载了它的两个副本,并且它们中的类型当然是不同的。AppDomain.AssemblyReslove当 IronPython 尝试加载它时,您可以通过挂钩/AppDomain.AssemblyLoad并返回本地程序集 ( )来解决此问题typeof (Entity.TestPy).Assembly,但我不保证这会起作用。

您不会遇到这种情况,System.Uri因为mscorlib.dll(可能还有其他一些系统程序集)由运行时特别处理。

更新: IronPython 常见问题解答指出,如果程序集尚未加载,则clr.AddReferenceToFile使用Assembly.LoadFile,它将加载到“既不”上下文中。尝试在调用 IronPython 之前访问一个方法Entity.TestPy以将程序集加载到默认Load上下文中。

于 2010-05-07T17:12:02.303 回答