C#4.0:将动态转换为静态 [英] C# 4.0: casting dynamic to static

查看:213
本文介绍了C#4.0:将动态转换为静态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个与另一个相关的分支问题,我问这里。我正在分解它,因为它真的是一个子问题:



我在设计类型为动态另一个(已知的)静态类型。



我有一个ironPython脚本,这样做:

  import clr 
clr.AddReference(System)
从系统导入*

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

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



现在在C#中,我正在新增脚本托管内容,并调用这个getter返回Uri对象:

  dynamic uri = scriptEngine.GetBclUri(); 
System.Uri u = uri as System.Uri; //将动态转换为静态罚款

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



然而....



现在我想定义自己的C#类,这个类将像在Uri中一样在动态地址中被新增。我简单的C#类:

 命名空间实体
{
public class TestPy //愚蠢的简单测试类我自己的
{
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(); // C#class

然后在C#中调用getter:

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

这里,演员无效。请注意,'test'对象(动态)是有效的 - 我可以调用DoSomething() - 它不会转换为已知的静态类型

  string s = test.DoSomething(asdf); //动态对象工作正常

所以我很困惑。 BCL类型System.Uri将从动态类型转换为正确的静态类型,但是我自己的类型不会。显然有一些我不知道这个...



-



更新:我做了一堆测试,以确保我的装配参考正确排列。我更改了引用的程序集编号,然后在C#中查看动态对象GetType()信息 - 它是正确的版本号,但仍然不会退回到已知的静态类型。



然后,我在控制台应用程序中创建了另一个类,以查看我会得到相同的结果,结果是正面的:我可以得到一个 c#中的动态引用到我的Python脚本中实例化的静态类型,但不会正确地退回到已知的静态类型。



-



更多信息:



Anton在下面提出AppDomain程序集绑定上下文是可能的罪魁祸首。做了一些测试后,我觉得很可能是。 。 。但我不知道如何解决它!我不知道程序集绑定的上下文,所以感谢安东,我已经变得更加接受程序集解决方案和微妙的错误出现在那里。



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

  private static类型pType = null; //这将是python类型ref 

//在脚本引擎启动之前,开始监视程序集分辨率
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)
{
//脚本引擎加载实体时汇编,得到一个引用
//到该类型,所以我们可以使用它来转换以后。
//这个类型引用神奇地携带它(不可见地只要我可以
//告诉)程序集绑定上下文
pType = args.LoadedAssembly.GetType(Entity.TestPy) ;
}
}

所以当python使用的类型是一样的在C#中,我正在考虑(正如Anton所提出的),不同的绑定上下文意味着对于运行时,两种类型(加载绑定上下文和loadfrom绑定上下文)是不同的 - 所以你所以现在我已经拥有了Python加载的类型(以及它的绑定上下文),并且在C#I中看到。可以将动态对象转换为静态类型,它的作用是:

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

string wow = pythonBoundContextObject .DoSomething(success);

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

  //在ctor中使用TestPy类的类... 
public class Foo
{
TestPy tp;

public Foo(TestPy t)
{
this.tp = t;
}
}

//无法传递pythonBoundContextObject(从上面):错误的绑定上下文
Foo f = new Foo(pythonBoundContextObject); //所有船上的失败船

所以分辨率将要在Python端:让脚本加载到正确的装配绑定上下文中。



在Python中,如果我这样做:

 #在我的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


This is an offshoot question that's related to another I asked here. I'm splitting it off because it's really a sub-question:

I'm having difficulties casting an object of type dynamic to another (known) static type.

I have an ironPython script that is doing this:

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

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

note that it's simply newing up a BCL System.Uri type and returning it. So I know the static type of the returned object.

now over in C# land, I'm newing up the script hosting stuff and calling this getter to return the Uri object:

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

Works no problem. I now can use the strongly typed Uri object as if it was originally instantiated statically.

however....

Now I want to define my own C# class that will be newed up in dynamic-land just like I did with the Uri. My simple C# class:

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

Now in Python, new up an object of this type and return it:

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

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

then in C# call the getter:

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

here, the cast does not work. Note that the 'test' object (dynamic) is valid--I can call the DoSomething()--it just won't cast to the known static type

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

so I'm perplexed. the BCL type System.Uri will cast from a dynamic type to the correct static one, but my own type won't. There's obviously something I'm not getting about this...

--

Update: I did a bunch of tests to make sure my assembly refs are all lining up correctly. I changed the referenced assembly ver number then looked at the dynamic objects GetType() info in C#--it is the correct version number, but it still will not cast back to the known static type.

I then created another class in my console app to check to see I would get the same result, which turned out positive: I can get a dynamic reference in C# to a static type instantiated in my Python script, but it will not cast back to the known static type correctly.

--

even more info:

Anton suggests below that the AppDomain assembly binding context is the likely culprit. After doing some tests I think it very likely is. . . but I can't figure out how to resolve it! I was unaware of assembly binding contexts so thanks to Anton I've become more educated on assembly resolution and the subtle bugs that crop up there.

So I watched the assembly resolution process by putting a handler on the event in C# prior to starting the script engine. That allowed me to see the python engine start up and the runtime start to resolve assemblies:

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

... and the handler sets the var pType to the Type that python is loading:

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

So while the type used by python is the same on in C#, I'm thinking (as proposed by Anton) that the different binding contexts mean that to the runtime, the two types (the one in the 'load binding context' and the 'loadfrom binding context) are different--so you can't cast on to the other.

So now that I have hold of the Type (along with it's binding context) loaded by Python, lo and behold in C# I can cast the dynamic object to this static type and it works:

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

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

But, sigh, this doesn't totally fix the problem, because the var pythonBoundContextObject while of the correct type, still carries the taint of the wrong assembly binding context. This means that I can't pass this to other parts of my code because we still have this bizzare type mismatch where the invisible specter of binding context stops me cold.

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

So the resolution is going to have to be on the Python side: getting the script to load in the right assembly binding context.

in Python, if I do this:

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

the runtime can't resolve my type:

import Entity.TestPy #fails

解决方案

Here's an answer from the IronPython team which covers the same problem:

C# / IronPython Interop with shared C# Class Library

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

这篇关于C#4.0:将动态转换为静态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆