C # 4.0: приведение динамического к статическому - PullRequest
12 голосов
/ 07 мая 2010

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

У меня возникают трудности при приведении объекта типа 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, как если бы он изначально создавался статически.

однако ....

Теперь я хочу определить свой собственный класс C #, который будет обновляться в динамической стране, как я это сделал с Uri. Мой простой 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!!!

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

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

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

-

Обновление: я провел кучу тестов, чтобы убедиться, что все ссылки на сборки правильно выстроены. Я изменил номер версии ссылочной сборки, затем посмотрел информацию о GetType () объектов dynamic в C # - это правильный номер версии, но он все равно не будет приведен к известному статическому типу.

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

-

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

Антон предполагает ниже, что вероятным виновником является контекст привязки сборки 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);

... и обработчик устанавливает переменную pType в тип, который загружает питон :

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

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

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

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

Но, вздох, это не решает проблему полностью, потому что переменная 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

Таким образом, разрешение должно быть на стороне Python: заставить скрипт загружаться в правильном контексте привязки сборки.

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

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

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

import Entity.TestPy #fails

Ответы [ 2 ]

3 голосов
/ 16 сентября 2010

Вот ответ от команды IronPython, который решает ту же проблему:

C # / IronPython Interop с общей библиотекой классов C #

(снято с http://lists.ironpython.com/pipermail/users-ironpython.com/2010-September/013717.html)

2 голосов
/ 07 мая 2010

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

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

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

...