5

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

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

实现此接口的类有一些约定,类的名称将是“OutputCacheVaryBy_______”,其中空白是从页面上的 varyByCustom 属性传入的值。另一个约定是 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 + varyByCustomTypeArg并且唯一的构造函数参数将是context我意识到我可以绕过需要这个美化的 if else 块并且可以只用Activator.

话虽如此,反射并不是我的强项,我知道Activator与静态创建和其他生成对象的方式相比,它的速度要慢得多。有什么理由我应该坚持使用这个当前的代码,或者我应该使用Activator或类似的方式来创建我的对象吗?

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

4

6 回答 6

6

如果反射对你来说太慢了。您可能可以让自己的 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(); 
        }
    }

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

于 2010-09-10T05:23:10.813 回答
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);

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

于 2010-09-10T23:05:45.997 回答
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 不是一个选项,我会让工厂创建具体的类,所以你永远不必担心你的实际方法。

于 2010-09-10T00:00:15.417 回答
1

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

也就是说,从您的问题看来,使用Activator可能对您有用。你测试过吗?真的太慢了​​吗?

或者,您可以将一堆工厂方法保存在Dictionary<string, Func<IOutputCacheVaryByCustom>. 如果您经常(在循环中)创建这些对象,我会使用它。然后,您还可以优化string您的密钥enum并完成转换。更抽象只会隐藏这段代码的意图......

于 2010-09-14T12:41:58.973 回答
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];
    }
于 2010-08-27T17:43:27.080 回答
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;
    } 

您可能必须在 typename 前面加上命名空间:"My.Name.Space.OutputCacheVaryBy"。如果这不起作用,请尝试使用程序集限定名称:

Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false)
于 2010-09-14T12:52:03.853 回答