Подтвердить что ты не робот

С# 4.0: литье динамическое в статическое

Это ответный вопрос, связанный с другим, который я спросил здесь. Я раскалываю его, потому что это действительно вопрос:

У меня возникают трудности с литьем объекта типа dynamic в другой (известный) статический тип.

У меня есть ironPython script, который делает это:

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

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

Обратите внимание, что он просто создает тип BCL System.Uri и возвращает его. Итак Я знаю статический тип возвращаемого объекта.

теперь на С# land, я новичок в хостинге script и вызываю этот getter, чтобы вернуть объект Uri:

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

Не работает проблема. Теперь я могу использовать строго типизированный объект Uri, как если бы он был первоначально создан статически.

однако....

Теперь я хочу определить свой собственный класс С#, который будет отображаться в динамической области так же, как и с Uri. Мой простой класс С#:

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

то в С# вызовите getter:

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

здесь актер не работает. Обратите внимание, что объект test (dynamic) действителен - я могу вызвать DoSomething() - , который просто не будет передан известному статическому типу

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

поэтому я недоумеваю. тип BCL System.Uri будет отбрасываться с динамического типа на правильный статический, но мой собственный тип не будет. Очевидно, что-то я не понимаю об этом...

-

Обновление. Я проверил множество тестов, чтобы убедиться, что мои сборки refs правильно выстроены. Я изменил ссылочный номер сборки, затем посмотрел на объект GetType() info dynamic на С# - это правильный номер версии, но он все равно не будет возвращен к известному статическому типу.

Затем я создал еще один класс в своем консольном приложении, чтобы проверить, что я получу тот же результат, который оказался положительным: я могу получить ссылку dynamic в С# на статический тип, созданный в моем Python script но он не вернется к известному статическому типу правильно.

-

еще больше информации:

Антон предлагает ниже, что контекст привязки сборки AppDomain является вероятным виновником. После нескольких тестов я думаю, что это очень вероятно., но я не могу понять, как его решить! Я не знал об условиях привязки к сборкам, поэтому благодаря Антону я стал более образованным в вопросах разрешения сборки и тонких ошибок, которые возникают там.

Итак, я просмотрел процесс разрешения сборки, поставив обработчик события на С# перед запуском движка script. Это позволило мне запустить запуск пула 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, тот же, что и на С#, я думаю (как было предложено Антоном), что различные контексты привязки означают, что для среды выполнения эти два типа (один в контексте привязки нагрузки 'и "loadfrom context context" ) - разные, поэтому вы не можете переключиться на другой.

Итак, теперь, когда я владею типом (вместе с ним связующим контекстом), загруженным Python, lo and behold в С#, я могу применить динамический объект к этому статическому типу, и он работает:

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

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

Но, вздох, это не полностью устраняет проблему, потому что var pythonBoundContextObject в то время как правильный тип по-прежнему несет в себе неправильный контекст привязки сборки. Это означает, что я не могу передать это другим частям моего кода, потому что у нас все еще есть это несоответствие типа bizzare, где невидимый призрак контекста привязки останавливает меня.

// 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: получение script для загрузки в правильном контексте привязки к узлу.

в Python, если я это сделаю:

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

среда выполнения не может разрешить мой тип:

import Entity.TestPy #fails
4b9b3361

Ответ 2

Я уверен, что IronPython загружает ваш entity.dll в другой контекст загрузки сборки, так что у вас есть две его копии, и типы в них, конечно, разные. Возможно, вам удастся обойти эту проблему, подключив AppDomain.AssemblyReslove/AppDomain.AssemblyLoad и вернув локальную сборку (typeof (Entity.TestPy).Assembly), когда IronPython пытается ее загрузить, но я не гарантирую, что это сработает.

Вы не испытываете этого с помощью System.Uri, потому что mscorlib.dll (и, возможно, некоторые другие системные сборки) обрабатывается специально средой выполнения.

Обновление: FAQ IronPython утверждает, что если сборка еще не загружена clr.AddReferenceToFile использует Assembly.LoadFile, который загружается в контекст "Ни один". Попробуйте получить доступ к методу от Entity.TestPy до вызова IronPython для загрузки сборки в контекст Load по умолчанию.