Проблема с созданием экземпляров типа с использованием отражения от сборки со строгим именем - PullRequest
5 голосов
/ 02 июня 2011

У меня есть существующее решение WinNET Forms .NET 2.0, которое развернуто на клиенте. Ради этого сценария, давайте назовем его «WinApp.exe». WinApp.exe имеет прямую ссылку на частную сборку с именем «Assembly.dll». Assembly.dll имеет прямую ссылку на другую сборку с именем «Framework.dll». В Framework.dll есть тип с именем «IPlugIn». Framework.dll не имеет строгого имени, но версия сборки 1.0. В Assembly.dll есть класс, который реализует IPlugIn из Framework.dll.

У меня есть другое решение .NET 2.0 Win Forms, которое называется Framework.exe. Этот проект Win Forms также имеет прямую ссылку на Framework.dll, для которой определен тип «IPlugIn». Framework.dll не имеет строгого имени, но версия этой сборки 2.0. Бит за битом, «IPlugIn» в Framework.dll v2 идентичен «IPlugIn» в Framework.dll v1. Код в Framework.exe использует отражение для загрузки реализации IPlugIn, которая находится в WinApp:

AssemblyName an = AssemblyName.GetAssemblyName("C:\\Program Files\\WinApp\\Assembly.dll");
Assembly dll = Assembly.Load(an);
object o = Activator.CreateInstance(dll.GetType("WinApp.ClassThatImplementsIPlugIn"));
IPlugIn iPlugIn = o as IPlugIn;

Пока все хорошо. Этот код работает! Теперь вот неприятная часть. Мне нужно назначить строгое имя Framework.dll v2, чтобы его можно было поместить в GAC. Однако WinApp и его зависимые сборки не будут повторно развернуты - он должен продолжать использовать те же версии сборок, которые он использует в настоящее время. Когда я даю Framework.dll v2 строгое имя, перекомпилирую и запускаю Framework.exe, при выполнении приведенного выше кода я получаю «InvalidCastException» в строке:

IPlugIn iPlugIn = o as IPlugIn;

Я думаю, что получаю это исключение, потому что теперь, когда у одной версии Framework.dll есть строгое имя, среда выполнения обрабатывает тип "IPlugIn", как будто это вообще другой тип. Мне нужно знать, есть ли способ решить эту проблему. Опять же, требования:

  1. Я должен быть в состоянии поставить Framework.dll v2 в GAC (поэтому он должен иметь строгое имя).
  2. WinApp должен продолжать работать так же, как в настоящее время (продолжайте ссылаться на Framework.dll v1). Я не могу распространять вновь скомпилированные сборки для WinApp.

Заранее спасибо!

Чад

1 Ответ

4 голосов
/ 02 июня 2011

Проблема здесь в том, что загружаются оба имени Framework со старым Framework. На самом деле .NET не волнует, что оба определения IPlugin совпадают. Они из разных сборок, поэтому они разные (я немного озадачен, почему он поднимет InvalidCastException, так как такой бросок просто вернет null в случае неудачи).

вариант A

Один из методов, который все еще будет использовать класс, будет использовать рефлексию, но это может иметь большое значение, когда в игру вступит больше типов, размещенных в Framework. все они должны быть доступны с помощью отражения. Вы можете создать оболочки, чтобы скрыть отражение, используя что-то вроде этого:

public class PluginWrapper : IPlugin
{
  object fObj;
  PropertyInfo fNameProperty;
  MethodInfo fGetOtherMethod;

  public PluginWrapper(object o)
  {
    fObj = o;
    fNameProperty = o.GetType().GetInterface("IPlugin").GetProperty("Name");
    fGetOtherMethod = o.GetType().GetInterface("IPlugin").GetMethod("GetOther", new Type[] { typeof(string) });
  }

  public string Name
  {
    get { return (string)fNameProperty.GetValue(fObj, null); }
  }

  public IOther GetOther(string name)
  {
    object result = fGetOtherMethod.Invoke(fObj, new object[] { name });

    if (result == null)
      return null;

    return new OtherWrapper(result);
  }
}

Тогда вы можете использовать свой объект следующим образом:

IPlugin iPlugIn = new PluginWrapper(o);

вариант B

Я могу придумать другой путь. Я не уверен, стоит ли вам идти по этой дороге, поскольку в моих глазах это выглядит как «хакерство», но я все равно поделюсь им и позволю вам решить.

Вы можете разрешить загрузку сборки, используя байтовый поток вместо AssemblyName. Таким образом, он не сможет разрешить другую Framework сборку и даст вам возможность внести изменения в событие AssemblyResolve:

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
Assembly dll = Assembly.Load(File.ReadAllBytes(@"C:\Program Files\WinApp\Assembly.dll"));
object o = Activator.CreateInstance(dll.GetType("WinApp.ClassThatImplementsIPlugIn"));
IPlugin iPlugIn = o as IPlugin;

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
  if (args.Name.ToLower().StartsWith("framework,"))
    return typeof(IPlugin ).Assembly;

  return null;
}

Это может быть немного сложнее, поскольку тем самым вы дадите .NET обещание, что сборка совместима и что вы готовы пойти на риск. Если что-то не совместимо, могут быстро возникнуть проблемы.

Другие опции

Возможно, могут быть и способы решения проблемы с использованием элемента bindingredirect в app.config. Я не совсем знаю подробности о том, как это работает, но, возможно, стоит немного разобраться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...