Лучший способ динамически создавать классы вместо использования блока переключателей - PullRequest
5 голосов
/ 27 августа 2010

В настоящее время моя функциональность VaryByCustom реализована в классах, реализующих интерфейс IOutputCacheVaryByCustom

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

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

В настоящее время я основываю это на enum и операторе switch, подобном

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 и просто создать экземпляр моего собственного объекта с помощью Activator.

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

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

Ответы [ 6 ]

6 голосов
/ 10 сентября 2010

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

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

Чем создать статический доступный только для чтения CloneDictionary, содержащий шаблоны объектов.что у вас уже есть для того, чтобы выбрать шаблон в словаре и вызвать NewObject ()

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

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

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

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

4 голосов
/ 11 сентября 2010

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

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

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

_factory[varyByCustomTypeArg](context);

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

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

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

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

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 не является опцией, я бы позволил фабрике создавать конкретные классы, поэтому вам никогда не придется беспокоиться о реальных методах.

1 голос
/ 14 сентября 2010

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

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

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

0 голосов
/ 14 сентября 2010

Используйте отражение.

    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;
    } 

Возможно, вам придется добавить префикс typename к пространству имен: "My.Name.Space.OutputCacheVaryBy" .Если это не сработает, попробуйте указать полное имя сборки:

Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false)
0 голосов
/ 27 августа 2010

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

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 - это имя моего статического класса, содержащего функции

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

    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];
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...