Der beste Weg, um dynamisch Klassen zu erstellen, anstatt einen Schalterblock der Verwendung
-
01-10-2019 - |
Frage
Zur Zeit habe ich meine VaryByCustom Funktionalität in Klassen implementiert, die eine Schnittstelle IOutputCacheVaryByCustom
public interface IOutputCacheVaryByCustom
{
string CacheKey { get; }
HttpContext Context { get; }
}
Eine Klasse diese Schnittstelle implementiert hat ein paar Konventionen der Name der Klasse „OutputCacheVaryBy_______“ sein, wo der Zuschnitt der Wert ist, der in der VaryByCustom Eigenschaft übergeben wird, auf den Seiten. Die andere Konvention ist, dass Kontext wird durch Konstruktor Injektion eingestellt werden.
Ich bin stützen diese Zeit aus einer Enum und einer 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;
}
Da ich weiß immer, dass die Klasse OutputCacheVaryBy + varyByCustomTypeArg
sein wird und die einzige Konstruktorargument context
wird mir klar, ich konnte Bypass, um diese, wenn auch Block verherrlichte und konnte nur mit Activator
meinem eigenen Objekt instanziiert.
Mit diesem wird gesagt, Reflexion ist nicht meine Stärke und ich weiß, dass Activator
wesentlich langsamer vergleichsweise statische Schöpfung und andere Möglichkeiten, um Objekte zu erzeugen. Gibt es einen Grund, warum ich mit diesem aktuellen Code bleiben soll oder ich Activator
oder eine ähnliche Art und Weise verwenden, um mein Objekt zu erstellen?
Ich habe den Blog gesehen http://www.smelser.net/blog/post/2010/03/05/When-Activator-is-just-to-slow.aspx aber ich bin nicht wirklich sicher, wie dies würde gelten, da ich mit Typen zur Laufzeit nicht statisch T arbeitete.
Lösung
Sie haben nicht wirklich brauchen, um Reflektion verwenden, da es sich um eine eher begrenzte Menge der möglichen Werte ist. Sie könnten jedoch so etwas wie dieses
tuninternal 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, aber es hält Aufbau und die Verwendung schön seperate
Andere Tipps
Wenn Reflexion für Sie zu langsam ist. Sie können sich wahrscheinlich Ihre eigenen Objectarbeits bekommen. Es ist wirklich einfach. Fügen Sie einfach eine neue Methode, um Ihre Schnittstelle.
public interface IOutputCacheVaryByCustom
{
string CacheKey { get; }
IOutputCacheVaryByCustom NewObject();
}
als eine statische erstellen Nur-Lese-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 nutzen, dass Sie bereits haben, um die Vorlage im Wörterbuch wählen und rufen NewObject ()
IOutputCacheVaryByCustom result =
cloneDictionary[VaryByCustomType.IsAuthenticated].NewObject();
Ist nur so einfach. Die NewObject () Methode, die Sie umsetzen müssen, wird eine neue Instanz zurückkehren, indem Sie das Objekt direkt zu erstellen.
public class OutputCacheVaryByIsAuthenticated: IOutputCacheVaryByCustom
{
public IOutputCacheVaryByCustom NewObject()
{
return new OutputCacheVaryByIsAuthenticated();
}
}
Das ist alles, was Sie benötigen. Und es ist unglaublich schnell.
Ich mag wirklich Objekterstellung Pflege von jemand anderem machen lassen. Zum Beispiel, wenn ich brauche verschiedene konkrete Implementierungen einer Schnittstelle ein IoC-Container hat Wunder für mich gearbeitet.
Als ein einfaches Beispiel Einheit verwendete, haben Sie einen Teil Konfigurationsschlüssel auf Implementierungen Verknüpfung wie folgt:
public void Register(IUnityContainer container)
{
container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByIsAuthenticated>("auth");
container.RegisterType<IOutputCacheVaryByCustom,OutputCacheVaryByRoles>("roles");
}
und Ihre Kreation aussehen würde viel einfacher in etwa 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 i eine Fabrik, die konkreten Klassen erstellen lassen würde, so müssen Sie nie Ihre eigentlichen Methoden um sie kümmern.
Bleiben Sie mit der switch-Anweisung. Wenn Sie nur ein paar mögliche Fälle wie diese haben, dann versuchen Sie einfach klug Abstraktion zu verwenden, um zu vermeiden Hinsetzen und bekommen die harten Teile des Programms Arbeits ...
Das heißt, aus Ihrer Frage scheint es für Sie, die Activator
Macht Arbeit verwenden. Haben Sie es getestet? War es wirklich zu langsam?
Alternativ könnten Sie einfach eine Reihe von Factory-Methoden in einem Dictionary<string, Func<IOutputCacheVaryByCustom>
halten. Das würde ich verwenden, wenn Sie diese Objekte oft erstellen (in einer Schleife). Sie könnten dann optimieren auch die string
Schlüssel zu Ihrem enum
und mit der Umstellung erfolgen. Möchten Sie mehr abstrakt wird verbergen nur die Absicht dieses Stück Code ...
Hier ist ein Beispiel 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 statische Klasse mit den Funktionen
Auch ein Beispiel, wo ich die Klasse erhalten enthalten ist, in eine 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];
}
Mit 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;
}
Unter Umständen müssen Sie Präfix Typnamen mit einem Namespace: "My.Name.Space.OutputCacheVaryBy" . Wenn das nicht funktioniert, versuchen Sie, mit einem Montage qualifizierten Namen:
Type.GetType("Name.Space.OutputCacheVaryBy" + varyByCustomTypeArg + ", AssemblyName", false)