动态创建类而不是使用 switch 块的最佳方法

发布于 2024-09-16 04:30:32 字数 1876 浏览 6 评论 0 原文

目前,我在实现接口 IOutputCacheVaryByCustom 的类中实现了 VaryByCustom 功能。

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

实现此接口的类有一些约定,该类的名称将为“OutputCacheVaryBy_______”,其中空白是传入的值来自页面上的 VariableByCustom 属性。另一个约定是 Context 将通过构造函数注入来设置。

目前,我基于一个枚举和一个类似于 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 + VarByCustomTypeArg 并且唯一的构造函数参数将是 context I意识到我可以绕过这个美化的 if else 块,并且可以使用 Activator 实例化我自己的对象。

话虽如此,反射并不是我的强项,而且我知道 Activator 与静态创建和其他生成对象的方法相比要慢得多。我是否应该坚持使用当前代码,或者应该使用 Activator 或类似的方式来创建对象?

我看过博客 http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx 但我不太确定这将如何应用,因为我在运行时使用类型而不是静态 T。

Currently I have my VaryByCustom functionality implemented in classes that implement an interface IOutputCacheVaryByCustom

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

A class implementing this interface has a few conventions the name of the class will be "OutputCacheVaryBy_______" where the blank is the value that is passed in from the varyByCustom property on pages. The other convention is that Context will be set through constructor injection.

Currently I'm basing this off an enum and a switch statement similar to

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

Since I always know that the class will be OutputCacheVaryBy + varyByCustomTypeArg and the only constructor argument will be context I realized I could bypass needing this glorified if else block and could just instantiate my own object with Activator.

With this being said, reflection is not my strong suit and I know that Activator is substantially slow comparatively to static creation and other ways to generate objects. Is there any reason why I should stick with this current code or should I use Activator or a similar way to create my object?

I've seen the blog http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx but I'm not really sure how this would apply since I'm working with types at runtime not static T.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

最后的乘客 2024-09-23 04:30:32

如果反射对你来说太慢。您也许可以让自己的 ObjectFactory 正常工作。
这真的很容易。只需向您的界面添加一个新方法即可。

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

然后创建一个保存对象模板的静态只读 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(); 
        }
    }

这就是您所需要的。而且速度快得令人难以置信。

If Reflection is too slow for you. You can probably get your own ObjectFactory working.
It's really easy. Just add a new method to your interface.

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

Than create a static readonly CloneDictionary that holds the object templates.

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

If you finished with that, you can use the enum that you already have in order to select the template in the dictionary and call NewObject()

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

Is just that simple. The NewObject() method you have to implement, will return a new Instance by directly creating the object.

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

That's all you need to have. And it's incredible fast.

新雨望断虹 2024-09-23 04:30:32

您实际上并不需要使用反射,因为它是一组相当有限的可能值。然而,你可以做这样的事情

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

,并用它代替你的创建逻辑,

_factory[varyByCustomTypeArg](context);

它不像开关那么快,但它保持构造和使用很好地分开

You don't really need to use reflection since it's a rather limited set of possible values. You could however do something like this

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

and substitute your creation logic with

_factory[varyByCustomTypeArg](context);

it's not as fast as a switch but it keeps construction and use nicely seperate

毁虫ゝ 2024-09-23 04:30:32

我真的很喜欢让其他人来处理对象创建。例如,如果我需要接口的不同具体实现,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 不是一个选项,我会让工厂创建具体的类,所以您永远不会有担心你对它们的实际方法。

I really like to have object creation taken care of by someone else. For example if i need different concrete implementations of an interface an IoC container has worked wonders for me.

As a simple example using unity you have a configuration portion linking up keys to implementations like so:

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

and your creation would look much simpler like so:

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

Or if IoC is not an option i would let a factory create the concrete classes, so you never have to worry your actual methods about them.

╭⌒浅淡时光〆 2024-09-23 04:30:32

继续使用 switch 语句。如果您只有几种可能的情况,那么您只是尝试使用巧妙的抽象来避免坐下来让程序的困难部分正常工作...

也就是说,从您的问题来看,它似乎使用 Activator 可能适合您。你测试过吗?真的是太慢了吗?

或者,您可以在 Dictionary 中保留一堆工厂方法。如果您经常(在循环中)创建这些对象,我会使用它。然后,您还可以优化 enumstring 键并完成转换。变得更抽象只会隐藏这段代码的意图......

Stay with the switch statement. If you only have a couple of possible cases like this, then you are just trying to use clever abstraction to avoid sitting down and getting the hard parts of your program working...

That said, from your question it seems using the Activator might work for you. Have you tested it? Was it really too slow?

Alternatively, you could just keep a bunch of factory methods in a Dictionary<string, Func<IOutputCacheVaryByCustom>. This I would use, if you are creating these objects often (in a loop). You could then also optimize the string key to your enum and be done with the conversion. Going more abstract will just hide the intent of this piece of code...

岛徒 2024-09-23 04:30:32

这是创建新对象的示例

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

Here is an example to create new object

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

Some infos:

  • cFunctions is the name of my static class containing the functions

Also an example where I get the class contained into an 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];
    }
残月升风 2024-09-23 04:30:32

使用反射。

    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)

Use reflection.

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

You might have to prefix typename with a namespace: "My.Name.Space.OutputCacheVaryBy". If that doesnt work, try with a assembly qualified name:

Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文