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