Дизайн: Управление сборками, вызываемыми через Activator.CreateInstance (...) - PullRequest
1 голос
/ 11 января 2011

В настоящее время я смотрю на оптимизацию среды приложения C # (.NET 3.5), и в настоящее время я смотрю на части кода, которые используют Activator.CreateInstance для вызова методов сборки через интерфейсы.В качестве примера у меня есть следующий, полностью функциональный код:

private object InvokeAssembly(string assemblyPath, string assemblyType, string data)
{
    Assembly assembly = Assembly.LoadFrom(assemblyPath);
    Type type = assembly.GetType(assemblyType, true, true);
    IMyInterface assemblyInterface = (IMyInterface)Activator.CreateInstance(type);

    return assemblyInterface.DoSomething(data);
}

Вопрос в том, хороший ли это дизайн?Особенно если учесть, что этот конкретный метод вызывается сотнями раз в минуту, будет ли следующий шаблон singleton-esque лучшим вариантом?

Улучшенный пример кода, предоставленный JaredPar

Dictionary<string, IMyInterface> assemblyCache = new Dictionary<string, IMyInterface>();

private object InvokeAssembly(string assemblyPath, string assemblyType, string data)
{
    var cacheKey = assemblyPath + "*" + assemblyType;

    IMyInterface assemblyInterface;

    if (!this.assemblyCache.TryGetValue(cacheKey, out assemblyInterface))
    {
        Assembly assembly = Assembly.LoadFrom(assemblyPath);
        Type type = assembly.GetType(assemblyType, true, true);
        assemblyInterface = (IMyInterface)Activator.CreateInstance(type);
        assemblyCache[cacheKey] = assemblyInterface;
    }

    return assemblyInterface.DoSomething(data);
}

Во втором примере интерфейс создается один раз и используется повторно, а не создается / собирается один раз за запрос.

Кажется логичным, что второй метод является лучшим решением, однако я более чем счастливСледует сказать, что это не имеет никакого значения.

Стоит также упомянуть, что этот метод и только этот метод работает на сборке

Ответы [ 3 ]

2 голосов
/ 11 января 2011

Второе решение будет работать лучше, но с ошибкой.Он кэширует экземпляр IMyInterface и повторно использует его при вызовах методов, не заботясь о том, чтобы в метод передавались одинаковые путь и тип сборки.Последовательные вызовы с различными значениями path или type приведут к использованию неправильного экземпляра IMyInterface.Шаблон кэширования должен знать об этом.

Один из способов сделать это - использовать Dictionary<string, IMyInterface> для кэширования типов.

Dictionary<string, IMyInterface> _map = new Dictionary<string, IMyInterface>();

private object InvokeAssembly(string assemblyPath, string assemblyType, string data)
{
  var key = assemblyPath + "*" + assemblyType;
  IMyInterface data;
  if (!_map.TryGetValue(key, out data)) {
    Assembly assembly = Assembly.LoadFrom(assemblyPath);
    Type type = assembly.GetType(assemblyType, true, true);
    data = (IMyInterface)Activator.CreateInstance(type);
    _map[key] = data;
  }
  return data;
}
1 голос
/ 11 января 2011

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

0 голосов
/ 11 января 2011

Я думаю, что второе в целом уступает - кэшируя экземпляр, созданный таким образом, вы рискуете любым состоянием, поддерживаемым реализацией этого интерфейса, взаимодействовать с остальной частью вашей программы потенциально непредсказуемым образом.,

Я полагаю, что вызовы Activator вызывают у вас проблемы с производительностью?В конце концов, CreateInstance - не самая быстрая вещь в книге - лучший способ сделать это - создать и кэшировать облегченные делегаты для конструкторов рассматриваемых типов и вызывать их.Вы можете сделать это через DynamicMethod - поискать в Google информацию о «динамическом методе отражения фабрик», и вы получите множество результатов, вот один из них .

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