C#4.0:将动态转换为静态

Kev*_*Won 12 c# ironpython dynamic-language-runtime dynamic

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

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

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

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

def GetBclUri():
    return Uri("http://google.com")
Run Code Online (Sandbox Code Playgroud)

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

现在在C#land,我正在新建脚本托管的东西,并调用此getter返回Uri对象:

dynamic uri = scriptEngine.GetBclUri();
System.Uri u = uri as System.Uri; // casts the dynamic to static fine
Run Code Online (Sandbox Code Playgroud)

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

然而....

现在我想定义我自己的C#类,它将在动态范围内新建,就像我对Uri一样.我简单的C#类:

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

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

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

def GetTest():
    return Entity.TestPy(); // the C# class
Run Code Online (Sandbox Code Playgroud)

然后在C#中调用getter:

dynamic test = scriptEngine.GetTest();
Entity.TestPy t = test  as Entity.TestPy; // t==null!!!
Run Code Online (Sandbox Code Playgroud)

在这里,演员表不起作用.注意'test'对象(动态)是有效的 - 我可以调用DoSomething() - 它只是不会强制转换为已知的静态类型

string s = test.DoSomething("asdf"); // dynamic object works fine
Run Code Online (Sandbox Code Playgroud)

所以我很困惑.BCL类型System.Uri将从动态类型转换为正确的静态类型,但我自己的类型不会.显然我没有得到这个......

-

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

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

-

更多信息:

Anton在下面建议AppDomain程序集绑定上下文可能是罪魁祸首.经过一些测试后我觉得很有可能...但我无法弄清楚如何解决它!我没有意识到装配绑定的背景,所以感谢安东我已经对装配解决方案以及那里出现的微妙错误进行了更多的教育.

所以我在启动脚本引擎之前通过在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);
Run Code Online (Sandbox Code Playgroud)

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

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

因此虽然python使用的类型在C#中是相同的,但我在想(正如Anton所提出的),不同的绑定上下文意味着对于运行时,两种类型('load binding context'中的一种类型和'loadfrom binding context)是不同的 - 所以你不能转向另一个.

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

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

string wow = pythonBoundContextObject .DoSomething("success");
Run Code Online (Sandbox Code Playgroud)

但是,感叹,这并不能完全解决问题,因为var pythonBoundContextObject正确的类型,仍然带有错误的程序集绑定上下文的污点.这意味着我无法将其传递给我的代码的其他部分,因为我们仍然有这种混淆类型不匹配,其中绑定上下文的隐形幽灵阻止我冷.

// 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
Run Code Online (Sandbox Code Playgroud)

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

在Python中,如果我这样做:

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

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

import Entity.TestPy #fails
Run Code Online (Sandbox Code Playgroud)