这是一个分支问题,与我问的另一个问题有关 here .我把它分开是因为它真的是一个子问题:
我在转换 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!!!
在这里,类型转换不起作用。请注意,'test' 对象(动态)是有效的——我可以调用 DoSomething()——它只是不会转换为已知的静态类型
string s = test.DoSomething("asdf"); // dynamic object works fine
所以我很困惑。 BCL 类型 System.Uri 将从动态类型转换为正确的静态类型,但我自己的类型不会。显然有一些我不明白的东西......
——
更新:我做了一堆测试,以确保我的程序集引用都正确排列。我更改了引用的程序集版本号,然后查看了
dynamic
C# 中的对象 GetType() 信息——它是正确的版本号,但它仍然不会转换回已知的静态类型。然后我在我的控制台应用程序中创建了另一个类来检查我是否会得到相同的结果,结果是肯定的:我可以得到
dynamic
在 C# 中引用在我的 Python 脚本中实例化的静态类型,但它不会正确转换回已知的静态类型。——
更多信息:
Anton 在下面建议 AppDomain 程序集绑定(bind)上下文可能是罪魁祸首。经过一些测试,我认为很有可能是。 . .但我不知道如何解决它!我不知道程序集绑定(bind)上下文,所以多亏了 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 所提议的)不同的绑定(bind)上下文意味着对于运行时,这两种类型(“加载绑定(bind)上下文”中的一种和'loadfrom 绑定(bind)上下文) 是不同的——所以你不能转换到另一个。
所以现在我已经掌握了 Python 加载的类型(以及它的绑定(bind)上下文),瞧,在 C# 中,我可以将动态对象转换为这个静态类型并且它可以工作:
dynamic test = scriptEngine.GetTest();
var pythonBoundContextObject =
Convert.ChangeType(test, pType); // pType = python bound
string wow = pythonBoundContextObject .DoSomething("success");
但是,唉,这并不能完全解决问题,因为 var
pythonBoundContextObject
而正确的类型,仍然带有错误的程序集绑定(bind)上下文的污点 .这意味着我不能把它传递给我代码的其他部分,因为我们还有这个 奇怪的类型不匹配绑定(bind)上下文的无形幽灵让我感到寒冷。// 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 端:让脚本加载到正确的程序集绑定(bind)上下文中。
在 Python 中,如果我这样做:
# in my python script
AppDomain.CurrentDomain.Load(
"Entity, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null");
运行时无法解析我的类型:
import Entity.TestPy #fails
最佳答案
以下是 IronPython 团队的回答,其中涵盖了相同的问题:
C# / IronPython Interop with shared C# Class Library
(摘自 http://lists.ironpython.com/pipermail/users-ironpython.com/2010-September/013717.html)
关于C# 4.0 : casting dynamic to static,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2785252/