C# 动态创建类而不是使用开关块的最佳方法

C# 动态创建类而不是使用开关块的最佳方法,c#,reflection,dynamic-class-creation,C#,Reflection,Dynamic Class Creation,目前,我在实现接口的类中实现了VaryByCustom功能 public interface IOutputCacheVaryByCustom { string CacheKey { get; } HttpContext Context { get; } } 一个实现这个接口的类有几个约定,该类的名称将是“OutPuxCaseVARY由Y.Y.Y.Y.Y.*”,其中空白是从页上的ValyByAuto属性传递的值。另一个约定是通过构造函数注入来设置上下文 目前,我基于一个enum

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

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

一个实现这个接口的类有几个约定,该类的名称将是“OutPuxCaseVARY由Y.Y.Y.Y.Y.*”,其中空白是从页上的ValyByAuto属性传递的值。另一个约定是通过构造函数注入来设置上下文

目前,我基于一个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;
}
公共重写字符串GetVaryByCustomString(HttpContext上下文,
字符串varyByCustomTypeArg)
{
//对于POST请求(回发),强制返回非缓存的输出
if(context.Request.RequestType.Equals(“POST”))
{
返回“post”+DateTime.Now.Ticks;
}
var varyByCustomType=EnumerationParser.Parse
(varyByCustomTypeArg).GetValueOrDefault();
ioutputcachecvarybycustom varyByCustom;
开关(varyByCustomType)
{
案例VaryByCustomType.IsAuthenticated:
varyByCustom=new outputCacheVaryBysAuthenticated(上下文);
打破
case VaryByCustomType。角色:
varyByCustom=新输出缓存varybyroles(上下文);
打破
违约:
抛出新ArgumentOutOfRangeException(“varyByCustomTypeArg”);
}
返回context.Request.Url.Scheme+varyByCustom.CacheKey;
}
因为我总是知道类将是
OutputCacheVaryBy+varyByCustomTypeArg
,唯一的构造函数参数将是
context
,所以我意识到我可以绕过这个美化的if-else块,只需用
Activator
实例化我自己的对象

尽管如此,反射并不是我的强项,我知道相对于静态创建和其他生成对象的方法,
Activator
的速度要慢得多。有什么理由我应该坚持使用当前代码,或者应该使用
Activator
或类似的方法来创建我的对象


我看过这个博客,但我不确定这将如何应用,因为我在运行时使用的是类型,而不是静态T。

下面是一个创建新对象的示例

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();
}
公共静态对象对象对象集(类型vClasseType)
{
返回typeof(cffunctions).GetMethod(“ObjectReturner2”).MakeGenericMethod(vClasseType).Invoke(null,新对象[]{});
}
公共静态对象ObjectReturner2(),其中T: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];
    }
publicstaticobjectobjret(类型vClasseType,ArrayList tArray,int-vIndex)
{
返回typeof(cffunctions).GetMethod(“ObjectReturner”).MakeGenericMethod(vClasseType).Invoke(null,新对象[]{tArray,vIndex});
}
公共静态对象ObjectReturner(ArrayList tArray,int-vIndex),其中T:new()
{
返回tArray[vIndex];
}

我真的很喜欢让其他人负责对象创建。例如,如果我需要一个接口的不同具体实现,一个IoC容器对我来说是个奇迹

作为使用unity的一个简单示例,您有一个配置部分将键链接到如下实现:

public void Register(IUnityContainer container)
{
   container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByIsAuthenticated>("auth");
   container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByRoles>("roles");
}
公共无效寄存器(IUnityContainer容器)
{
container.RegisterType(“auth”);
container.RegisterType(“角色”);
}
您的创作看起来会简单得多,如下所示:

//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;
}
//以某种形式注入
专用只读IUnityContainer\u容器;
公共重写字符串GetVaryByCustomString(HttpContext上下文,
字符串varyByCustomTypeArg)
{
//对于POST请求(回发),强制返回非缓存的输出
if(context.Request.RequestType.Equals(“POST”))
{
返回“post”+DateTime.Now.Ticks;
}
尝试
{
IOutputCacheVaryByCustomom varyByCustomom=\u container.Resolve(varyByCustomTypeArg,新的DependencyOverride(上下文));
}
捕获(异常exc)
{
抛出新ArgumentOutOfRangeException(“varyByCustomTypeArg”,exc);
}
返回context.Request.Url.Scheme+varyByCustom.CacheKey;
}

或者如果IoC不是一个选项,我会让工厂创建具体的类,这样你就不用担心你的实际方法了。

如果反射对你来说太慢了。您可能可以让自己的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()方法将通过直接创建对象返回一个新实例

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

这就是你所需要的。而且速度惊人。

您实际上不需要使用反射,因为它是一组非常有限的可能值。但是你可以这样做

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

它的速度不如switch,但它保持了构造和使用的良好分离性

继续使用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;   
        }

        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)

这就是我过去能够将不同的类包含到需要类似转换的arraylist中的方法。这些是静态类/方法,但是
    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;
    } 
Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false)