Как создать высокопроизводительные классы .NET, используя отражение? - PullRequest
9 голосов
/ 11 июля 2009

Хорошо, мы все знаем, что Reflecttion во много раз менее эффективен, чем "обновление" экземпляра класса, и во многих случаях это просто отлично, в зависимости от требований приложения.

ВОПРОС: Как мы можем создать высокопроизводительные классы .NET, используя стратегию позднего связывания (Reflection).

У меня есть существующее требование, которое требует, чтобы экземпляры классов создавались с использованием отражения (CreateInstance), но производительность критична. В моей ситуации я создаю экземпляры для каждого входящего SMS-сообщения в нашем приложении. Во время производства это может быть более миллиона в день.

Я хотел бы услышать и поделиться некоторыми идеями о том, как создавать классы .NET без прямой ссылки на классы в коде, например, используя Reflection. Я также подумал, есть ли способ как-то кешировать класс Factory, который может улучшить время создания

Ответы [ 6 ]

12 голосов
/ 11 июля 2009

1 миллион в день - это не много; Я бы просто использовал Activator.CreateInstance (быстрый тест с использованием Activator.CreatInstance(Type) показывает, что на моем скромном ноутбуке он может создавать объекты 1M из Type за ~ 2 с).

Мысли о быстром создании объектов:

  • использовать дженерики и ограничение : new() (ноль усилий)
  • используйте DynamicMethod и напишите IL (не сложно)

Здесь показана реализация подхода new (без внешнего ограничения : new()): ObjectFactory.cs.

Пример IL см. dapper-dot-net и il.Emit(OpCodes.Newobj, ...)

6 голосов
/ 11 июля 2009

Я не думаю, что миллион в день - это слишком много для простого размышления. Я считаю, что вы чрезмерно оптимизируете, но в любом случае, как вы сказали, просто создайте фабричный класс, используя один вызов Activator.CreateInstance, и кешируйте его. Фактические экземпляры будут созданы с использованием вызова метода CreateInstance() для возвращаемого объекта.

public interface IClassFactory {
    IClass CreateInstance();
}

public interface IClass {
   // your actual class interface.
}

public class DefaultClassFactory : IClassFactory {
    public IClass CreateInstance() {
        return new DefaultClass(); // the implementation class
    }
}

Где-то у вас будет поле static типа IClassFactory, которое вы будете устанавливать один раз с экземпляром DefaultClassFactory или любыми другими классами, указанными в файле конфигурации или чем-то еще.

5 голосов
/ 11 июля 2009

Как обычно, Джон Скит - твой друг здесь. См. Его сообщение в блоге Размышление и изучение делегатов

5 голосов
/ 11 июля 2009

Некоторые мысли:

  • Держите по одному экземпляру каждого класса, как только обнаружите, что он вам нужен. Затем вместо CreateInstance клонируйте его.
  • После создания первого экземпляра сохраняйте тип экземпляра. затем используйте Activator.CreateInstance (Type)

Кэшируйте экземпляр для клонирования или тип в Dictionary<string,Type> или Dictionary<string,object>.

1 голос
/ 11 июля 2009

ВЕЛИКИЙ! Подход Class Factory, кажется, является подходящим способом.

Используя комбинацию Assembly.CreateInstance(typeNameString) в первом запросе, затем кэшируйте Type на заводе.

При последующих вызовах используйте Activator.CreateInstance(type).

При использовании этого подхода он на 20% медленнее, чем при использовании собственного оператора New. Ничего страшного!

Статистика для создания 10 миллионов Employee объектов следующим образом:

  • 8 секунд с использованием оператора new

  • 10 секунд с использованием подхода Factory / Type / Cache.

Вот пример кода, если кому-то интересно:

private IEmployee CachedClassFactory()
{
    if(_typeCache == null)
    {
        // This is a one time hit to load the type into the cache
        string typeName = "ClassFactoryTest.Employee";
        string assemblyName = "ClassFactoryTest";
        Assembly assembly = Assembly.Load(assemblyName);
        IEmployee employee = assembly.CreateInstance(typeName) as IEmployee;                        
        _typeCache = employee.GetType();
    }

    IEmployee instance = Activator.CreateInstance(_typeCache) as IEmployee;

    instance.FirstName = "Raiford";
    instance.LastName = "Brookshire";
    instance.Birthdate = DateTime.Now.AddYears(-35);
    instance.Age = 35;

    return instance;    
}
0 голосов
/ 11 июля 2009

Определите и реализуйте интерфейс вместо использования отражения.

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