2010-08-26 10 views
5

Actualmente tengo mi funcionalidad VaryByCustom implementado en las clases que implementan una interfaz IOutputCacheVaryByCustommejor manera de crear dinámicamente clases en lugar de utilizar un bloque de interruptores

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

Una clase que implementa esta interfaz tiene una serie de convenciones el nombre de la clase será "OutputCacheVaryBy_______" donde el espacio en blanco es el valor que se pasa desde la propiedad varyByCustom en las páginas. La otra convención es que Contexto se establecerá a través de la inyección de constructor.

Actualmente estoy basando esta fuera una enumeración y una sentencia switch similar a

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

Desde siempre sé que la clase será OutputCacheVaryBy + varyByCustomTypeArg y el único argumento del constructor será context me di cuenta que podía pasar por alto necesidad este glorificado si no más bloquea y podría simplemente crear una instancia de mi propio objeto con Activator.

Dicho esto, la reflexión no es mi fuerte y sé que Activator es sustancialmente lento comparativamente a la creación estática y otras formas de generar objetos. ¿Hay alguna razón por la cual debería seguir con este código actual o debería usar Activator o una forma similar de crear mi objeto?

he visto el blog http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx pero no estoy muy seguro de cómo esto se aplicaría ya que estoy trabajando con tipos en tiempo de ejecución no T. estática

+0

¿Los objetos son caros (lentos) para crear? ¿Se requiere el contexto durante la construcción? ¿Se puede establecer a través de la propiedad Contexto? La respuesta a estas preguntas es necesaria para proporcionar la mejor solución. –

+0

No hay objetos triviales para crear, y el contexto se establecería de manera óptima en el constructor porque esa es una dependencia central de la clase, pero podría exponerse y establecerse en la propiedad, pero eso deja espacio para los NRE. –

+0

¿qué versión de C# estás usando? –

Respuesta

4

Realmente no necesita usar la reflexión ya que es un conjunto bastante limitado de valores posibles. sin embargo, puede hacer algo como esto

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

y sustituir la lógica de su creación con

_factory[varyByCustomTypeArg](context); 

no es tan rápido como un interruptor pero mantiene la construcción y el uso de bien separada

0

Aquí hay un ejemplo para crear nuevo objeto

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

Algunas informaciones:

  • cFunctions es el nombre de mi clase estática que contiene las funciones

también un ejemplo donde consigo la clase contenida en un 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

Esto es lo que solía ser capaz de obtener diferentes clases contenidas en una lista de arrays que necesitaba una transformación similar. Estas son clases/métodos estáticos, pero también podrían ser normales, eran estáticas solo porque era una clase genérica utilizada como dll. Estoy seguro de que descubrirá cómo adaptarlo a sus necesidades. – Wildhorn

3

Me gustaría tener la creación de objetos a cargo de otra persona. Por ejemplo, si necesito diferentes implementaciones concretas de una interfaz, un contenedor de IoC me ha hecho maravillas.

Como un simple ejemplo utilizando la unidad tiene una parte de configuración de la vinculación de las llaves a las implementaciones de este modo:

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

y su creación se vería mucho más simple de este modo:

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

O si COI No es una opción, dejaría que una fábrica cree las clases concretas, por lo que nunca tendrá que preocuparse por sus métodos reales.

6

Si la reflexión es demasiado lenta para usted. Probablemente pueda hacer funcionar su propia ObjectFactory. Es realmente fácil. Solo agrega un nuevo método a tu interfaz.

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

Que crear un solo de lectura estática CloneDictionary que contiene las plantillas de objetos.

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

Si usted tiene que con eso, puede utilizar la enumeración que ya tiene el fin de seleccionar la plantilla en el diccionario y llamar NewObject()

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

Es así de sencillo. El método NewObject() que debe implementar devolverá una nueva instancia creando directamente el objeto.

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

Eso es todo lo que necesita tener. Y es increíblemente rápido.

1

estadía con el conmutador declaración. Si solo tiene un par de casos posibles como este, entonces solo está tratando de usar la abstracción inteligente para evitar sentarse y conseguir que las partes más duras de su programa funcionen ...

Dicho esto, de su pregunta parece que está usando el Activator podría funcionar para usted. ¿Lo has probado? ¿De verdad fue muy lento?

Como alternativa, puede mantener un montón de métodos de fábrica en un Dictionary<string, Func<IOutputCacheVaryByCustom>. Esto lo usaría, si está creando estos objetos a menudo (en un bucle). También puede optimizar la clave string en su enum y finalizar la conversión. Ir más abstracto ocultará el propósito de este fragmento de código ...

0

Use la reflexión.

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

Es posible que tenga el prefijo nombretipo con un espacio de nombres: "My.Name.Space.OutputCacheVaryBy". Si eso no funciona, intente con un nombre calificado de ensamblaje:

Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false) 
Cuestiones relacionadas