2010-08-26 4 views
5

Zur Zeit habe ich meine VaryByCustom Funktionalität in Klassen implementiert, die eine Schnittstelle IOutputCacheVaryByCustomDer beste Weg, um dynamisch Klassen zu erstellen, anstatt einen Schalter Block mit

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

Eine Klasse der Umsetzung diese Schnittstelle implementieren hat ein paar Konventionen die Namen der Klasse wird "OutputCacheVaryBy_______" sein, wobei das Leerzeichen der Wert ist, der von der varyByCustom-Eigenschaft auf Seiten übergeben wird. Die andere Konvention ist, dass Context durch Konstruktorinjektion gesetzt wird.

Derzeit bin stützen ich das eine Enumeration und eine switch-Anweisung ähnlich wie

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

off Da ich weiß immer, dass die Klasse OutputCacheVaryBy + varyByCustomTypeArg sein wird und der einzige Konstruktorargument wird context mir klar, ich umgehen konnte, um Dies verherrlicht, wenn sonst blockieren und könnte nur mein eigenes Objekt mit Activator instanziieren.

Mit diesem gesagt, ist Reflexion nicht meine starke Farbe und ich weiß, dass Activator im Vergleich zu statischen Erstellung und anderen Möglichkeiten, Objekte zu generieren, im Wesentlichen langsam ist. Gibt es einen Grund, warum ich bei diesem aktuellen Code bleiben sollte oder sollte ich Activator oder eine ähnliche Methode verwenden, um mein Objekt zu erstellen?

Ich habe den Blog http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx gesehen, aber ich bin nicht wirklich sicher, wie dies gelten würde, da ich mit Typen zur Laufzeit nicht statisch T.

+0

Sind die Objekte teuer (zeitaufwändig) zu erstellen? Ist der Kontext während der Erstellung erforderlich, kann er stattdessen über die Context-Eigenschaft festgelegt werden? Die Antwort auf diese Fragen ist erforderlich, um die beste Lösung zu bieten. –

+0

Nein die Objekte trivial zu erstellen, und der Kontext wäre optimal im Konstruktor festgelegt, da dies eine Kernabhängigkeit der Klasse ist, aber es könnte ausgesetzt und auf der Eigenschaft festgelegt werden, aber das lässt Raum für NREs. –

+0

Welche Version von C# verwenden Sie? –

Antwort

4

Sie müssen die Reflexion nicht wirklich verwenden, da es sich um eine ziemlich begrenzte Menge möglicher Werte handelt. Man könnte so etwas wie dieses

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

und ersetzen Sie Ihre Kreation Logik mit

_factory[varyByCustomTypeArg](context); 

es ist nicht so schnell wie ein Schalter jedoch tun, aber es hält Konstruktion und verwenden schön separate

0

Hier ist ein Beispiel bin arbeite neues Objekt erstellen

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

Einige Infos:

  • cFunctions ist der Name meiner statischen Klasse enthält die Funktionen

auch ein Beispiel, wo ich die Klasse in eine Arraylist enthalten sind erhalten:

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

Dies war, was ich früher in der Lage war, verschiedene Klassen in eine Arraylist zu bekommen, die eine ähnliche Transformation benötigten. Dies sind statische Klassen/Methoden, aber sie könnten auch normal sein, sie waren nur statisch, weil es sich um eine generische Klasse handelte, die als DLL verwendet wurde. Ich bin mir sicher, dass Sie herausfinden werden, wie Sie es für Ihre Bedürfnisse anpassen können. – Wildhorn

3

Ich mag wirklich Objekterstellung betreut von jemand anderem haben. Zum Beispiel, wenn ich verschiedene konkrete Implementierungen einer Schnittstelle brauche, hat ein IoC-Container Wunder für mich bewirkt.

Als einfaches Beispiel Einheit verwenden Sie einen Abschnitt Konfigurationsschlüssel auf Implementierungen wie so Verknüpfung:

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

und Ihre Kreation aussehen würde viel einfacher wie 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; 
} 

Oder wenn IoC ist keine Option, ich würde eine Fabrik die konkreten Klassen erstellen lassen, so dass Sie sich nie um Ihre tatsächlichen Methoden kümmern müssen.

6

Wenn Reflexion für Sie zu langsam ist. Sie können wahrscheinlich Ihre eigene ObjectFactory zum Laufen bringen. Es ist wirklich einfach. Fügen Sie einfach eine neue Methode zu Ihrer Schnittstelle hinzu.

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

Dann erstellen Sie eine statische readonly CloneDictionary, die die Objektvorlagen enthält.

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

Wenn Sie damit fertig sind, können Sie die ENUM verwenden, die Sie bereits haben, um die Vorlage im Wörterbuch zu wählen und rufen NewObject()

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

nur so einfach. Die NewObject() -Methode, die Sie implementieren müssen, gibt eine neue Instanz zurück, indem Sie das Objekt direkt erstellen.

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

Das ist alles was Sie brauchen. Und es ist unglaublich schnell.

1

Bleiben Sie mit dem Schalter Erklärung. Wenn Sie nur ein paar mögliche Fälle wie diese haben, dann versuchen Sie nur, clevere Abstraktion zu verwenden, um zu vermeiden, die harten Teile Ihres Programms zu arbeiten ...

Das sagte, von Ihrer Frage scheint es zu verwenden die Activator könnte für Sie arbeiten. Hast du es getestet? War es wirklich zu langsam?

Alternativ könnten Sie einfach eine Reihe von Factory-Methoden in einer Dictionary<string, Func<IOutputCacheVaryByCustom> halten. Dies würde ich verwenden, wenn Sie diese Objekte oft (in einer Schleife) erstellen. Sie könnten dann auch den string Schlüssel zu Ihrem enum optimieren und mit der Konvertierung fertig sein. Gehen abstrakter wird nur die Absicht dieses Stück Code verbergen ...

0

Verwenden Sie Reflexion.

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

Sie könnten Typname mit einem Namespace Präfix haben: "My.Name.Space.OutputCacheVaryBy". Wenn das nicht funktioniert, versuchen Sie es mit einem qualifizierten Namen der Baugruppe:

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