我理解困难的是:我应该在构建方法中做些什么?
我们的目的是实现一个框架,该框架具有一个方法,该方法生成一个域类的新实例,其中的属性和方法添加了功能。 以下示例介绍了创建域Stock对象的情况,其中传递给build的参数将作为参数传递给它实例化中的stock的构造函数。
Enhancer.Build<Stock>("Apple", "Dow Jones");
添加的功能由标记虚拟方法和属性的自定义属性指定,如以下示例所示。在注释中说明我们打算使用功能Build of Enhancer从返回的对象中开始验证标记的方法和属性。 示例中的每个自定义属性都应具有公共基本类型 - EnhancedAtributte - 使用抽象方法Check(object [] args)从标记方法接收参数。
class Stock
{
public Stock(string name, string index) { ... }
[NonNull]
public virtual string Market { get; set; } // set will launch an exception if values are null
[Min(73)]
public virtual long Quote { get; set; } // set will launch an exception if values are < 73
[Min(0.325)]
public virtual double Rate { get; set; } // set will launch an exception if values are < 0.325
[Accept("Jenny", "Lily", "Valery")]
public virtual string Trader{get; set; } // set will launch an exception if values are different than Jenny, Lily or Valery
[Max(58)]
public virtual int Price { get; set; } // set will launch an exception if values are > 58
// it will launch exception if the state of this or any of the parameters has been altered by the execution of a marked method
// -- BuildInterest
[NoEffects]
public double BuildInterest(Portfolio port, Store st) { ... }
}
答案 0 :(得分:0)
我首先要验证T
既不是封闭的也不是抽象的。这应该足以确保它是(1)一个类; (2)能够延长。
接下来,迭代typeof(T).GetProperties()
以查找CanWrite
为true
且property.GetCustomAttributes<EnhancedAtributte>().Any()
报告true
的所有“增强”属性。如果没有任何匹配的属性,您可以直接实例化T
。
由于属性实例本身验证了属性值,因此您需要在某处缓存属性,以免在每次属性更改时产生昂贵的查找。你应该瞄准生成一个看起来像这样的类:
public class __Enhanced__Stock__ {
private static readonly EnhancedAttribute[] __Price__Attributes__;
static __Enhanced__Stock__() {
__Price__Attributes__ = typeof(Stock).GetProperty("Price")
.GetCustomAttributes<EnhancedAtributte>()
.ToArray();
}
public override int Price {
get => base.Price;
set {
for (int i = 0, n = __Price__Attributes__.Length; i < n; i++)
__Price__Attributes__[i].Check(new Object[] { (Object)value });
}
base.Price = value;
}
}
}
可以从TypeBuilder
创建ModuleBuilder
,AssemblyBuilder
是从TypeBuilder.DefineField
创建的。对于后两者,你可以保留一个静态实例。
您需要使用EnhancedAttribute[][]
定义静态字段,以用作每个属性的属性缓存(或使用属性索引的单个TypeBuilder.DefineTypeInitializer
)。在任何一种情况下,您都必须定义一个类构造函数(请参阅MethodBuilder.GetILGenerator()
)来初始化缓存。您必须使用TypeBuilder.DefineProperty
自己编写IL。
对于您找到的每个增强属性,您需要定义一个具有相同名称的新属性(请参阅get
),并为每个属性发出单独的set
和TypeBuilder.DefineMethod
访问者(见PropertyBuilder.SetGetMethod
)。每个访问者都需要绑定到属性,这可以通过SetSetMethod
和T
来完成。您还必须在TypeBuilder.DefineMethodOverride
中使访问者覆盖访问者,您可以通过get
执行访问。您可以在覆盖属性in this question上看到一些提示。
set
访问器覆盖的代码很简单:只需要委托给base属性。 Check
访问器更复杂,因为您需要遍历属性的属性缓存并调用每个属性的for
方法。同样,您需要手动发出IL,其中包括确定如何发出简单的Check
循环。或者,由于您已经知道每个属性的属性数,因此您可以编写一个手动展开的循环。无论如何,对于每次拨打object[]
,请记住您需要初始化一个新的value
,其中必须复制CreateType()
参数。
一旦声明了属性缓存字段,类型初始值设定项,属性及其访问器,就基本完成了。您可以通过调用TypeBuilder
上的public class Enhancer
{
private static readonly ModuleBuilder ModuleBuilder;
static Enhancer()
{
var b = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName(Guid.NewGuid().ToString()),
AssemblyBuilderAccess.Run);
ModuleBuilder = b.DefineDynamicModule($"{b.GetName().Name}.Module");
}
private const BindingFlags InstanceFlags = BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic;
private const FieldAttributes CacheFlags = FieldAttributes.Private |
FieldAttributes.Static |
FieldAttributes.InitOnly;
private const TypeAttributes TypeFlags = TypeAttributes.Public |
TypeAttributes.Sealed |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout |
TypeAttributes.AnsiClass;
private static IEnumerable<PropertyInfo> FindEnhancedProperties(Type t)
{
foreach (var p in t.GetProperties(InstanceFlags))
{
if (p.CanWrite &&
p.GetSetMethod(true).IsVirtual &&
p.IsDefined(typeof(EnhancedAttribute)))
{
yield return p;
}
}
}
public static EnhancedAttribute[] FindEnhancedAttributes(PropertyInfo p)
{
return p.GetCustomAttributes<EnhancedAttribute>().ToArray();
}
private static readonly MethodInfo CheckMethod =
typeof(EnhancedAttribute).GetMethod(
nameof(EnhancedAttribute.Check),
new[] { typeof(object[]) });
private static readonly MethodInfo GetTypeFromHandleMethod =
typeof(Type).GetMethod(
nameof(Type.GetTypeFromHandle),
new[] { typeof(RuntimeTypeHandle) });
private static readonly MethodInfo GetPropertyMethod =
typeof(Type).GetMethod(
nameof(Type.GetProperty),
new[] { typeof(string), typeof(BindingFlags) });
private static readonly MethodInfo FindEnhancedAttributesMethod =
typeof(Enhancer).GetMethod(
nameof(FindEnhancedAttributes),
new[] { typeof(PropertyInfo) });
private readonly Type _base;
private readonly TypeBuilder _enhanced;
private readonly PropertyInfo[] _properties;
private readonly FieldBuilder[] _attributeCaches;
private readonly MethodBuilder[] _propertySetters;
private static readonly Dictionary<Type, Type> Cache = new Dictionary<Type, Type>();
public static T Build<T>(params object[] args) where T : class
{
Type type;
lock (Cache)
{
if (!Cache.TryGetValue(typeof(T), out type))
Cache[typeof(T)] = type = new Enhancer(typeof(T)).Enhance();
}
return (T)Activator.CreateInstance(type, args);
}
private Enhancer(Type t)
{
if (t?.IsSealed != false || t.IsInterface)
{
throw new ArgumentException(
"Type must be a non-sealed, non-abstract class type.");
}
_base = t;
_enhanced = ModuleBuilder.DefineType($"<Enhanced>{t.FullName}", TypeFlags, t);
_properties = FindEnhancedProperties(t).ToArray();
_attributeCaches = _properties.Select(
p => _enhanced.DefineField(
$"__{p.Name}Attributes",
typeof(EnhancedAttribute[]),
CacheFlags)).ToArray();
_propertySetters = new MethodBuilder[_properties.Length];
}
private Type Enhance()
{
GenerateTypeInitializer();
for (int i = 0, n = _properties.Length; i < n; i++)
EnhanceProperty(i);
GenerateConstructors();
return _enhanced.CreateType();
}
private void GenerateConstructors()
{
var baseCtors = _base.GetConstructors(InstanceFlags);
foreach (var baseCtor in baseCtors)
{
if (baseCtor.IsPrivate)
continue;
var parameters = baseCtor.GetParameters();
var ctor = _enhanced.DefineConstructor(
baseCtor.Attributes,
baseCtor.CallingConvention,
parameters.Select(p => p.ParameterType).ToArray());
var il = ctor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < parameters.Length; i++)
il.Emit(OpCodes.Ldarg, i + 1);
il.Emit(OpCodes.Call, baseCtor);
il.Emit(OpCodes.Ret);
}
}
private void GenerateTypeInitializer()
{
var typeInit = _enhanced.DefineTypeInitializer();
var il = typeInit.GetILGenerator();
for (int i = 0, n = _properties.Length; i < n; i++)
{
var p = _properties[i];
il.Emit(OpCodes.Ldtoken, _base);
il.EmitCall(OpCodes.Call, GetTypeFromHandleMethod, null);
il.Emit(OpCodes.Ldstr, p.Name);
il.Emit(OpCodes.Ldc_I4_S, (int)InstanceFlags);
il.EmitCall(OpCodes.Call, GetPropertyMethod, null);
il.EmitCall(OpCodes.Call, FindEnhancedAttributesMethod, null);
il.Emit(OpCodes.Stsfld, _attributeCaches[i]);
}
il.Emit(OpCodes.Ret);
}
private void EnhanceProperty(int index)
{
var p = _properties[index];
var property = _enhanced.DefineProperty(
p.Name,
p.Attributes,
p.PropertyType,
null);
var baseSet = p.GetSetMethod(true);
var set = _enhanced.DefineMethod(
baseSet.Name,
baseSet.Attributes & ~MethodAttributes.NewSlot | MethodAttributes.Final,
baseSet.CallingConvention,
baseSet.ReturnType,
new[] { p.PropertyType });
property.SetSetMethod(set);
_enhanced.DefineMethodOverride(set, baseSet);
var il = set.GetILGenerator();
var attributeCount = p.GetCustomAttributes<EnhancedAttribute>().Count();
for (int j = 0; j < attributeCount; j++)
{
il.Emit(OpCodes.Ldsfld, _attributeCaches[index]);
il.Emit(OpCodes.Ldc_I4, j);
il.Emit(OpCodes.Ldelem_Ref, j);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_1);
if (p.PropertyType.IsValueType)
il.Emit(OpCodes.Box, p.PropertyType);
il.Emit(OpCodes.Stelem_Ref);
il.EmitCall(OpCodes.Callvirt, CheckMethod, null);
}
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Call, baseSet, null);
il.Emit(OpCodes.Ret);
_propertySetters[index] = set;
}
}
来“烘焙”派生类型。
我觉得要写一些代码,所以这是一个应该处理基于属性的属性验证的解决方案。我并不完全清楚其他方法的属性应如何工作,但这应该是一个很好的起点。
array_combine