2010-08-26 4 views
5

В настоящее время я моя функциональность VaryByCustom реализованы в классах, которые реализуют интерфейс IOutputCacheVaryByCustomЛучший способ динамически создавать классы, вместо использования переключателя блока

public interface IOutputCacheVaryByCustom 
{ 
    string CacheKey { get; } 
    HttpContext Context { get; } 
} 

класс, реализующий этот интерфейс имеет несколько соглашений имя класса будет «OutputCacheVaryBy_______», где пробел - это значение, которое передается из свойства variableByCustom на страницах. Другое соглашение состоит в том, что Контекст будет установлен через инъекцию конструктора.

В настоящее время я основывая это от перечисления и переключатель утверждения, аналогичного

public override string GetVaryByCustomString(HttpContext context, 
               string varyByCustomTypeArg) 
{ 
    //for a POST request (postback) force to return back a non cached output 
    if (context.Request.RequestType.Equals("POST")) 
    { 
     return "post" + DateTime.Now.Ticks; 
    } 
    var varyByCustomType = EnumerationParser.Parse<VaryByCustomType?> 
          (varyByCustomTypeArg).GetValueOrDefault(); 


    IOutputCacheVaryByCustom varyByCustom; 
    switch (varyByCustomType) 
    { 
     case VaryByCustomType.IsAuthenticated: 
      varyByCustom = new OutputCacheVaryByIsAuthenticated(context); 
      break; 
     case VaryByCustomType.Roles: 
      varyByCustom = new OutputCacheVaryByRoles(context); 
      break; 
     default: 
      throw new ArgumentOutOfRangeException("varyByCustomTypeArg"); 
    } 

    return context.Request.Url.Scheme + varyByCustom.CacheKey; 
} 

Так как я всегда знаю, что класс будет OutputCacheVaryBy + varyByCustomTypeArg и единственным конструктор аргумент будет context я понял, что может обойти нуждаясь этот прославленный if else block и может просто создать экземпляр моего объекта с помощью Activator.

С учетом этого отражение не является моим сильным костюмом, и я знаю, что Activator существенно медленнее относительно статического создания и других способов генерации объектов. Есть ли причина, почему я должен придерживаться этого текущего кода, или я должен использовать Activator или аналогичный способ создания моего объекта?

Я видел блог http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx, но я действительно не знаю, как это будет применяться, так как я работаю с типами во время выполнения не статична Т.

+0

Являются ли объекты дорогими (требующими много времени) для создания? Требуется ли контекст во время строительства, может ли он быть установлен через свойство Context? Ответ на эти вопросы необходим, чтобы обеспечить наилучшее решение. –

+0

Нет объектов тривиальных для создания, и контекст будет оптимально установлен в конструкторе, потому что это основная зависимость от класса, однако он может быть выставлен и установлен на свойство, но это оставляет место для NRE. –

+0

какая версия C# вы используете? –

ответ

4

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

internal class Factory<T,Arg> 
{ 
    Dictionary<string,Func<Arg.T>> _creators; 
    public Factory(IDictionary<string,Func<Arg,T>> creators) 
    { 
    _creators = creators; 
    } 
} 

и заменить логику создания с

_factory[varyByCustomTypeArg](context); 

это не так быстро, как переключатель, но он сохраняет конструкцию и использовать хорошо индивидуальный

0

Вот пример для создания нового объекта

public static object OBJRet(Type vClasseType) 
{ 
    return typeof(cFunctions).GetMethod("ObjectReturner2").MakeGenericMethod(vClasseType).Invoke(null, new object[] { }); 
} 

public static object ObjectReturner2<T>() where T : new() 
{ 
    return new T(); 
} 

Некоторые информация о:

  • cFunctions это имя моего статического класса, содержащего функции

Также пример, где я получаю класс, содержащийся в ArrayList:

public static object OBJRet(Type vClasseType, ArrayList tArray, int vIndex) 
    { 
     return typeof(cFunctions).GetMethod("ObjectReturner").MakeGenericMethod(vClasseType).Invoke(null, new object[] { tArray, vIndex }); 
    } 

    public static object ObjectReturner<T>(ArrayList tArray, int vIndex) where T : new() 
    { 
     return tArray[vIndex]; 
    } 
+0

Это то, что я имел обыкновение иметь, чтобы получить другой класс, содержащийся в arraylist, который нуждался в подобной трансформации. Это статические классы/методы, но они также могут быть нормальными, они были статическими только потому, что это был общий класс, используемый как dll. Я уверен, что вы поймете, как адаптировать его для ваших нужд. – Wildhorn

3

Я действительно хотел бы иметь создание объекта позаботилось о ком-то другим. Например, если мне нужны разные конкретные реализации интерфейса, то ядро ​​IoC создало для меня чудеса.

В качестве простого примера использования единства у вас есть часть конфигурации, связывающую ключи к реализации, как так:

public void Register(IUnityContainer container) 
{ 
    container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByIsAuthenticated>("auth"); 
    container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByRoles>("roles"); 
} 

и ваше творение будет выглядеть намного проще так:

//injected in some form 
private readonly IUnityContainer _container; 

public override string GetVaryByCustomString(HttpContext context, 
               string varyByCustomTypeArg) 
{ 
    //for a POST request (postback) force to return back a non cached output 
    if (context.Request.RequestType.Equals("POST")) 
    { 
     return "post" + DateTime.Now.Ticks; 
    } 
    try 
    { 
    IOutputCacheVaryByCustom varyByCustom = _container.Resolve<IOutputCacheVaryByCustom>(varyByCustomTypeArg, new DependencyOverride<HttpContext>(context)); 
    } 
    catch(Exception exc) 
    { 
     throw new ArgumentOutOfRangeException("varyByCustomTypeArg", exc); 
    } 
    return context.Request.Url.Scheme + varyByCustom.CacheKey; 
} 

Или если IoC не вариант, я бы позволил фабрике создавать конкретные классы, поэтому вам никогда не придется беспокоиться о своих реальных методах.

6

Если отражение слишком медленное для вас. Вероятно, вы можете использовать свою собственную ObjectFactory. Это действительно легко. Просто добавьте новый метод в свой интерфейс.

public interface IOutputCacheVaryByCustom 
    { 
     string CacheKey { get; } 
     IOutputCacheVaryByCustom NewObject(); 
    } 

Than создать статический cloneDictionary для чтения, содержащий шаблоны объектов.

static readonly 
     Dictionary<VaryByCustomType, IOutputCacheVaryByCustom> cloneDictionary 
     = new Dictionary<VaryByCustomType, IOutputCacheVaryByCustom> 
     { 
      {VaryByCustomType.IsAuthenticated, new OutputCacheVaryByIsAuthenticated{}}, 
      {VaryByCustomType.Roles, new OutputCacheVaryByRoles{}}, 
     }; 

Если вы закончили с этим, вы можете использовать перечисление, что у вас уже есть для того, чтобы выбрать шаблон в словаре и называют NewObject()

 IOutputCacheVaryByCustom result = 
      cloneDictionary[VaryByCustomType.IsAuthenticated].NewObject(); 

ли просто. Метод NewObject(), который вы должны реализовать, вернет новый экземпляр, непосредственно создав объект.

public class OutputCacheVaryByIsAuthenticated: IOutputCacheVaryByCustom 
    { 
     public IOutputCacheVaryByCustom NewObject() 
     { 
      return new OutputCacheVaryByIsAuthenticated(); 
     } 
    } 

Это все, что вам нужно. И это невероятно быстро.

1

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

Это говорит о том, что с вашего вопроса, похоже, используется Activator может работать для вас. Вы протестировали его? Это было слишком медленно?

В качестве альтернативы, вы можете просто сохранить кучу заводских методов в Dictionary<string, Func<IOutputCacheVaryByCustom>. Это я бы использовал, если вы часто создаете эти объекты (в цикле). Затем вы также можете оптимизировать ключ string до вашего enum и сделать это с помощью преобразования. Более абстрактное будет просто скрывать намерение этой части кода ...

0

Использование отражения.

public override string GetVaryByCustomString(HttpContext context, 
              string varyByCustomTypeArg) 
    { 
     //for a POST request (postback) force to return back a non cached output 
     if (context.Request.RequestType.Equals("POST")) 
     { 
      return "post" + DateTime.Now.Ticks; 
     } 

     Type type = Type.GetType("OutputCacheVaryBy" + varyByCustomTypeArg, false) 
     if (type == null) 
     { 
      Console.WriteLine("Failed to find a cache of type " + varyByCustomTypeArg); 
      return null; 
     } 

     var cache = (IOutputCacheVaryByCustom)Activator.CreateInstance(type, new object[]{context}); 
     return context.Request.Url.Scheme + cache.CacheKey; 
    } 

Вы могли бы иметь префикс имяТипа с пространством имен: "My.Name.Space.OutputCacheVaryBy". Если это не сработает, попробуйте получить квалифицированное имя сборки:

Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false) 
Смежные вопросы