2010-08-26 3 views
5

Attualmente ho la mia funzionalità VaryByCustom implementato in classi che implementano l'interfaccia IOutputCacheVaryByCustommodo migliore per creare dinamicamente le classi invece di utilizzare un blocco switch

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

Una classe che implementa questa interfaccia ha poche convenzioni il nome della classe sarà "OutputCacheVaryBy_______" dove lo spazio vuoto è il valore che viene passato dalla proprietà varyByCustom alle pagine. L'altra convenzione è che il contesto verrà impostato tramite l'iniezione del costruttore.

Attualmente mi sto basando questa via un enum e uno switch simile 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; 
} 

Dal So sempre che la classe sarà OutputCacheVaryBy + varyByCustomTypeArg e l'unico argomento del costruttore sarò context ho capito che potevo escludere che necessitano questo glorificato se altro blocca e potrebbe semplicemente istanziare il mio stesso oggetto con Activator.

Con questo detto, la riflessione non è il mio forte seme e so che Activator è sostanzialmente lento rispetto alla creazione statica e altri modi per generare oggetti. C'è qualche ragione per cui dovrei attenermi a questo codice attuale o dovrei usare Activator o un modo simile per creare il mio oggetto?

ho visto il blog http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx, ma io non sono davvero sicuro di come questo si applica dal momento che sto lavorando con i tipi in fase di esecuzione T. non statica

+0

Gli oggetti costosi (richiedono molto tempo) da creare? È il contesto richiesto durante la costruzione, può invece essere impostato tramite la proprietà Contesto? La risposta a queste domande è necessaria per fornire la soluzione migliore. –

+0

Nessun oggetto banale da creare e il contesto dovrebbe essere impostato in modo ottimale nel costruttore perché è una dipendenza principale della classe, tuttavia potrebbe essere esposto e impostato sulla proprietà ma lascerà spazio per gli NRE. –

+0

quale versione di C# stai usando? –

risposta

4

Non è davvero necessario utilizzare il riflesso poiché è un insieme piuttosto limitato di valori possibili. Si potrebbe tuttavia fare qualcosa di simile

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

e sostituire la logica creazione con

_factory[varyByCustomTypeArg](context); 

non è veloce come un interruttore, ma mantiene la costruzione e l'uso ben separata

0

Ecco un esempio per creare nuovo oggetto

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

Alcune informazioni:

  • cFunctions è il nome della mia classe statica che contiene le funzioni

anche un esempio in cui ho la classe contenuta in 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

Questo è quello che ero solito essere in grado di ottenere una classe diversa contenuta in un arraylist che richiedeva una trasformazione simile. Questi sono metodi di classe statici, ma potrebbero anche essere normali, erano statici solo perché si trattava di una classe generica utilizzata come dll. Sono sicuro che capirai come adattarlo alle tue esigenze. – Wildhorn

3

Mi piacerebbe davvero avere la creazione di oggetti curato da qualcun altro. Ad esempio, se ho bisogno di diverse implementazioni concrete di un'interfaccia, un contenitore di IoC ha fatto miracoli per me.

Come semplice esempio utilizzando l'unità si dispone di una porzione di configurazione collegandosi chiavi per implementazioni in questo modo:

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

e la vostra creazione apparirebbe molto più semplice in questo 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 se IOC non è un'opzione vorrei lasciare che una fabbrica crei le lezioni concrete, quindi non devi mai preoccuparti dei tuoi metodi reali su di loro.

6

Se Reflection è troppo lento per te. Probabilmente puoi far funzionare il tuo ObjectFactory. È davvero facile. Basta aggiungere un nuovo metodo alla tua interfaccia.

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

Creare una statica in sola lettura CloneDictionary che contiene i modelli di oggetto.

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

Se avete finito con questo, è possibile utilizzare l'enum che hai già, al fine di selezionare il modello nel dizionario e chiamare NewObject()

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

è proprio così semplice. Il metodo NewObject() che devi implementare, restituirà una nuova istanza creando direttamente l'oggetto.

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

Questo è tutto ciò che serve. Ed è incredibilmente veloce.

1

Resta con l'interruttore dichiarazione. Se hai solo un paio di casi possibili come questo, stai solo cercando di usare un'astrazione intelligente per evitare di sederti e ottenere le parti difficili del tuo programma ...

Detto questo, dalla tua domanda sembra che stia usando il Activator potrebbe funzionare per voi. L'hai provato? Era davvero troppo lento?

In alternativa, è possibile mantenere un po 'di metodi di fabbrica in un Dictionary<string, Func<IOutputCacheVaryByCustom>. Questo vorrei usare, se stai creando questi oggetti spesso (in un ciclo). È inoltre possibile ottimizzare la chiave string per il proprio enum e terminare la conversione. Andando più astratto si nasconde semplicemente l'intento di questo pezzo di codice ...

0

Utilizzare la riflessione.

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

Potrebbe essere necessario anteporre typename con uno spazio dei nomi: "My.Name.Space.OutputCacheVaryBy". Se questo non funziona, prova con un nome qualificato dell'assemblaggio:

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