C# 如果vs重载vs反射
我有很多的课程,包括:C# 如果vs重载vs反射,c#,reflection,polymorphism,C#,Reflection,Polymorphism,我有很多的课程,包括: class C1 { [PropName("Prop1")] public string A {get;set;} [PropName("Prop2")] public string B {get;set;} [PropName("Prop3")] public string C {get;set;} } class C2 { [PropName("Prop1")] public string D {
class C1
{
[PropName("Prop1")]
public string A {get;set;}
[PropName("Prop2")]
public string B {get;set;}
[PropName("Prop3")]
public string C {get;set;}
}
class C2
{
[PropName("Prop1")]
public string D {get;set;}
[PropName("Prop2")]
public string E {get;set;}
[PropName("Prop3")]
public string F {get;set;}
}
该属性说明实际属性是什么,但C#属性的名称并不总是匹配。对于C1和C2,C1.A与C2.D具有相同的性质
这些类不是任何继承链的一部分,我无法控制它们,因此无法更改它们
对于“Prop1”、“Prop2”、…,有一些常见的操作,“PropN”。编写这些操作时不需要太多的代码重复,但又能使其保持可维护性的最佳解决方案是什么
解决方案#1(if语句-大量)
解决方案#2(重载-很多)
解决方案#3(反射)-我担心perf,因为这些操作中的每一个都会被调用数千次,并且有几百个操作
void OperationWithProp1(object o)
{
// Pseudo code:
// Get all properties from o that have the PropName attribute
// Look if any attribute matches "Prop1"
// Get the value of the property that matches
// Do something with the value of the property
}
您会选择哪种解决方案?为什么?你还有其他的模式吗
编辑澄清: 很多班级意味着有几十个
很多属性意味着30-40个属性/类IMO,为了清晰性/可维护性,使用重载。如果有很多重叠的代码,请将其分解为单独的方法
也就是说,我假设您首先关心的是可维护性,因为您没有提到速度 您可以创建一个包装类,公开所需的属性,并包装实际的
C1
和C2
类的实例。一种方法是通过学员:
interface WithProperties {
string A {get;set;}
string B {get;set;}
string C {get;set;}
}
class WrappedCX<T> : WithProperties {
private readonly T wrapped;
private readonly Func<T,string> getA;
private readonly Action<T,string> setA;
private readonly Func<T,string> getB;
private readonly Action<T,string> setB;
private readonly Func<T,string> getC;
private readonly Action<T,string> setC;
public WrappedCX(T obj, Func<T,string> getA, Action<T,string> setA, Func<T,string> getB, Action<T,string> setB, Func<T,string> getC, Action<T,string> setC) {
wrapped = obj;
this.getA = getA;
this.setA = setA;
this.getB = getB;
this.setB = setB;
this.getC = getC;
this.setC = setC;
}
public string A {
get {return getA(wrapped);}
set {setA(wrapped, value);}
}
public string B {
get {return getB(wrapped);}
set {setB(wrapped, value);}
}
public string C {
get {return getC(wrapped);}
set {setC(wrapped, value);}
}
}
此时,w1
和w2
都实现了通用的WithProperties
接口,因此您可以在不检查其类型的情况下使用它们
为了让人觉得有趣,用一个构造函数替换七个参数的构造函数,该构造函数接受一个
obj
参数,通过反射获取其类,检查其属性中定义的自定义属性,并创建/编译与属性a
的getter和setter对应的LINQ表达式,B
和C
。这将允许您构造WrappedCX
,而不会在调用中拖尾丑陋的lambdas。这里的折衷是,现在lambda将在运行时构造,因此缺少属性的可能编译错误将成为运行时异常。为了获得最佳性能,您应该为每个属性编写一对静态方法,其形式如下:
[PropName("Prop1")]
static string Prop1Getter(thisType it) { return it.WhateverProperty; }
[PropName("Prop1")]
static string Prop1Setter(thisType it, string st) { it.WhateverProperty = st; }
我建议您然后使用反射来生成委托,并使用静态泛型类来缓存它们。实际上,您将拥有一个私有静态类PropertyAccessors
,其委托声明如下:
const int numProperties = 3;
public Func<T, string>[] Getters;
public Action<T, string>[] Setters;
const int numProperties=3;
公共职能[]获取者;
公共行动制定者;
然后,静态构造函数将执行以下操作:
Getters = new Func<T, string>[numProperties];
Setters = new Action<T, string>[numProperties];
for (int i = 0; i< numProperties; i++)
{
int ii = i; // Important--ensure closure is inside loop
Getters[ii] = (T it) => FindSetAndRunGetter(ii, it);
Setters[ii] = (T it, string st) => FindSetAndRunSetter(ii, it, st);
}
Getters=newfunc[numproperty];
Setters=新动作[numProperties];
对于(int i=0;iFindSetAndRunGetter(ii,it);
Setters[ii]=(T it,string st)=>findSetandUnset(ii,it,st);
}
FindSetAndRunGetter(ii,it)
方法应该搜索适当的属性getter,如果找到,则将getter[ii]
设置为指向适当的属性getter,运行一次,并返回结果findsetandrunseter(ii,it,st)
应该对属性设置器执行类似的操作,将st
作为参数运行一次
使用这种方法将结合使用反射的多功能性和“自动升级”(意味着在将来的类中自动找到方法的能力),其速度与硬编码方法相当(如果不是更好的话)。一个麻烦是需要定义如上所述的静态方法。可能可以使用
Reflection.Emit
自动生成包含此类方法的静态类,但这超出了我的专业水平。您可以动态生成代理类,使用属性化的“PropName”名称访问正确的成员。在生成对属性的调用之前,还需要检测属性是否实际实现了get/set。也可能是一种更复杂的方法来保证生成的代理的唯一类型名
有关用法,请参见Main(),Main下面是OperationWithProp1()的实现
(这里有很多代码)
公共接口IC
{
字符串Prop1{get;set;}
字符串Prop2{get;set;}
字符串Prop3{get;set;}
}
公共类别C1
{
[项目名称(“项目1”)]
公共字符串A{get;set;}
[PropName(“Prop2”)]
公共字符串B{get;set;}
[PropName(“Prop3”)]
公共字符串C{get;set;}
}
公共级C2
{
[项目名称(“项目1”)]
公共字符串D{get;set;}
[PropName(“Prop2”)]
公共字符串E{get;set;}
[PropName(“Prop3”)]
公共字符串F{get;set;}
}
公共类代理生成器
{
私有静态只读字典_proxyClasses=new Dictionary();
私有静态只读AssemblyName _AssemblyName=新的AssemblyName(“ProxyBuilderClass”);
私有静态只读AssemblyBuilder _AssemblyBuilder=AppDomain.CurrentDomain.DefinedDynamicAssembly(_assemblyName,AssemblyBuilderAccess.RunAndSave);
私有静态只读ModuleBuilder _ModuleBuilder=_assemblyBuilder.DefinedDynamicModule(_assemblyName.Name,_assemblyName.Name+“.dll”);
公共静态void SaveProxyAssembly()
{
_assemblyBuilder.Save(_assemblyName.Name+“.dll”);
}
公共静态类型GetProxyTypeForBackingType(类型proxyInterface,类型backingType)
{
var key=Tuple.Create(proxyInterface,backingType);
类型返回类型;
if(_proxyClasses.TryGetValue(键,out returnType))
返回类型;
var typeBuilder=\u moduleBuilder.DefineType(
“ProxyClassProxies。”+“Proxy”+proxyInterface.Name+“\u To”+backingTy
[PropName("Prop1")]
static string Prop1Getter(thisType it) { return it.WhateverProperty; }
[PropName("Prop1")]
static string Prop1Setter(thisType it, string st) { it.WhateverProperty = st; }
const int numProperties = 3;
public Func<T, string>[] Getters;
public Action<T, string>[] Setters;
Getters = new Func<T, string>[numProperties];
Setters = new Action<T, string>[numProperties];
for (int i = 0; i< numProperties; i++)
{
int ii = i; // Important--ensure closure is inside loop
Getters[ii] = (T it) => FindSetAndRunGetter(ii, it);
Setters[ii] = (T it, string st) => FindSetAndRunSetter(ii, it, st);
}
public interface IC
{
string Prop1 { get; set; }
string Prop2 { get; set; }
string Prop3 { get; set; }
}
public class C1
{
[PropName("Prop1")]
public string A { get; set; }
[PropName("Prop2")]
public string B { get; set; }
[PropName("Prop3")]
public string C { get; set; }
}
public class C2
{
[PropName("Prop1")]
public string D { get; set; }
[PropName("Prop2")]
public string E { get; set; }
[PropName("Prop3")]
public string F { get; set; }
}
public class ProxyBuilder
{
private static readonly Dictionary<Tuple<Type, Type>, Type> _proxyClasses = new Dictionary<Tuple<Type, Type>, Type>();
private static readonly AssemblyName _assemblyName = new AssemblyName("ProxyBuilderClasses");
private static readonly AssemblyBuilder _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.RunAndSave);
private static readonly ModuleBuilder _moduleBuilder = _assemblyBuilder.DefineDynamicModule(_assemblyName.Name, _assemblyName.Name + ".dll");
public static void SaveProxyAssembly()
{
_assemblyBuilder.Save(_assemblyName.Name + ".dll");
}
public static Type GetProxyTypeForBackingType(Type proxyInterface, Type backingType)
{
var key = Tuple.Create(proxyInterface, backingType);
Type returnType;
if (_proxyClasses.TryGetValue(key, out returnType))
return returnType;
var typeBuilder = _moduleBuilder.DefineType(
"ProxyClassProxies." + "Proxy_" + proxyInterface.Name + "_To_" + backingType.Name,
TypeAttributes.Public | TypeAttributes.Sealed,
typeof (Object),
new[]
{
proxyInterface
});
//build backing object field
var backingObjectField = typeBuilder.DefineField("_backingObject", backingType, FieldAttributes.Private);
//build constructor
var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] {backingType});
var ctorIL = ctor.GetILGenerator();
ctorIL.Emit(OpCodes.Ldarg_0);
var ctorInfo = typeof (Object).GetConstructor(types: Type.EmptyTypes);
ctorIL.Emit(OpCodes.Call, ctorInfo);
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Ldarg_1);
ctorIL.Emit(OpCodes.Stfld, backingObjectField);
ctorIL.Emit(OpCodes.Ret);
foreach (var targetPropertyInfo in backingType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var propertyName = targetPropertyInfo.Name;
var attributes = targetPropertyInfo.GetCustomAttributes(typeof (PropName), true);
if (attributes.Length > 0 && attributes[0] != null)
propertyName = (attributes[0] as PropName).Name;
var propBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, targetPropertyInfo.PropertyType, null);
const MethodAttributes getSetAttrs =
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual;
//build get method
var getBuilder = typeBuilder.DefineMethod(
"get_" + propertyName,
getSetAttrs,
targetPropertyInfo.PropertyType,
Type.EmptyTypes);
var getIL = getBuilder.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, backingObjectField);
getIL.EmitCall(OpCodes.Callvirt, targetPropertyInfo.GetGetMethod(), Type.EmptyTypes);
getIL.Emit(OpCodes.Ret);
propBuilder.SetGetMethod(getBuilder);
//build set method
var setBuilder = typeBuilder.DefineMethod(
"set_" + propertyName,
getSetAttrs,
null,
new[] {targetPropertyInfo.PropertyType});
var setIL = setBuilder.GetILGenerator();
setIL.Emit(OpCodes.Ldarg_0);
setIL.Emit(OpCodes.Ldfld, backingObjectField);
setIL.Emit(OpCodes.Ldarg_1);
setIL.EmitCall(OpCodes.Callvirt, targetPropertyInfo.GetSetMethod(), new[] {targetPropertyInfo.PropertyType});
setIL.Emit(OpCodes.Ret);
propBuilder.SetSetMethod(setBuilder);
}
returnType = typeBuilder.CreateType();
_proxyClasses.Add(key, returnType);
return returnType;
}
public static TIProxy CreateProxyObject<TIProxy>(object backingObject, out TIProxy outProxy) where TIProxy : class
{
var t = GetProxyTypeForBackingType(typeof (TIProxy), backingObject.GetType());
outProxy = Activator.CreateInstance(t, backingObject) as TIProxy;
return outProxy;
}
private static void Main(string[] args)
{
var c1 = new C1();
IC c1Proxy;
CreateProxyObject(c1, out c1Proxy);
var c2 = new C2();
IC c2Proxy;
CreateProxyObject(c2, out c2Proxy);
c1Proxy.Prop1 = "c1Prop1Value";
Debug.Assert(c1.A.Equals("c1Prop1Value"));
c2Proxy.Prop1 = "c2Prop1Value";
Debug.Assert(c2.D.Equals("c2Prop1Value"));
//so you can check it out in reflector
SaveProxyAssembly();
}
private static void OperationWithProp1(object o)
{
IC proxy;
CreateProxyObject(o, out proxy);
string prop1 = proxy.Prop1;
// Do something with prop1
}