12

これは、私がここで尋ねた別の質問に関連する別の質問です。それは本当にサブ質問なので、私はそれを分割しています:

dynamicタイプのオブジェクトを別の (既知の) 静的タイプにキャストするのに問題があります。

これを行う IronPython スクリプトがあります。

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

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

BCL の System.Uri 型を新しく作成して返すだけであることに注意してください。だから私は返されたオブジェクトの静的な型を知っています。

C# の世界では、スクリプトをホストするものを新たに作成し、このゲッターを呼び出して Uri オブジェクトを返します。

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

問題なく動作します。厳密に型指定された Uri オブジェクトを、最初に静的にインスタンス化されたかのように使用できるようになりました。

でも....

ここで、Uri で行ったのと同じように、動的ランドで新しく作成される独自の C# クラスを定義したいと考えています。私の単純な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# でゲッターを呼び出します。

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 は動的型から正しい静的型にキャストされますが、自分の型はキャストされません。明らかにこれについて私が得ていないものがあります...

--

更新: アセンブリ参照がすべて正しく並んでいることを確認するために、一連のテストを行いました。参照されているアセンブリのバージョン番号を変更してからdynamic、C# でオブジェクトの GetType() 情報を調べました。これは正しいバージョン番号ですが、既知の静的型にキャスト バックしません。

次に、コンソール アプリで別のクラスを作成して、同じ結果が得られることを確認しましたが、結果は肯定的でしたdynamic。Python スクリプトでインスタンス化された静的型への参照を C# で取得できますが、クラスにキャスト バックしません。既知の静的型が正しく。

--

さらに詳しい情報:

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 がロードしている 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");
    }
}

したがって、python で使用される型は C# でも同じですが、(Anton によって提案されているように) 異なるバインディング コンテキストは、ランタイムにとって 2 つのタイプ (「ロード バインディング コンテキスト」のタイプと'loadfrom バインディング コンテキスト) は異なるため、他のものにキャストすることはできません。

これで、Python によって読み込まれた Type (バインディング コンテキストと共に) を取得できたので、見よ、C# で動的オブジェクトをこの静的型にキャストでき、動作します。

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

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

しかし、ため息、これで問題が完全に解決されるわけではありません。なぜなら、pythonBoundContextObject正しい型の varが、間違ったアセンブリ バインディング context の汚染をまだ持っているからです。これは、これをコードの他の部分に渡すことができないことを意味します。バインディング コンテキストの目に見えない亡霊が私を冷たく止める、この奇妙な型の不一致がまだあるからです。

// 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別のアセンブリ ロード コンテキストにロードするので、2 つのコピーがロードされ、それらの型はもちろん異なります。AppDomain.AssemblyReslove/AppDomain.AssemblyLoadをフックして、IronPython がロードしようとしたときにローカル アセンブリ ( ) を返すことで、この問題を回避できる可能性がありますがtypeof (Entity.TestPy).Assembly、これが機能することは保証しません。

(およびおそらく他のシステム アセンブリ) がランタイムによって特別に処理されるSystem.Uriため、これは発生しません。mscorlib.dll

更新: IronPython FAQは、アセンブリがまだ読み込まれていない場合、「どちらでもない」コンテキストに読み込まれる をclr.AddReferenceToFile使用すると述べています。IronPython を呼び出す前にAssembly.LoadFileメソッドにアクセスして、アセンブリをデフォルトのコンテキストにロードしてみてください。Entity.TestPyLoad

于 2010-05-07T17:12:02.303 に答える