[英]How can I create a derived type from T, using System.Reflection.Emit
what I am having difficulties understanding is : what should I do in the build method?我难以理解的是:我应该在构建方法中做什么?
It is our intent to implement a framework that has a method that generates a new instance of a class of domain in which their properties and methods have added functionalities.我们的目的是实现一个框架,该框架具有一个方法,该方法生成域类的新实例,其中的属性和方法添加了功能。 The Following example presents a case of the creation of a object of domain Stock where the parameters passed to build will be pass as arguments to the constructor of stock in it's instantiation.
下面的示例展示了创建域 Stock 对象的情况,其中传递给 build 的参数将作为参数传递给 Stock 实例化中的构造函数。
Enhancer.Build<Stock>("Apple", "Dow Jones");
The added functionalities are specified by custom attributes that mark the virtual methods and properties, as the following example shows.添加的功能由标记虚拟方法和属性的自定义属性指定,如以下示例所示。 In the comments it is said what we intend the marked methods and properties to start verifying in the returned object from the function Build of Enhancer.
在评论中说我们打算在从 Enhancer 的函数 Build 返回的对象中开始验证标记的方法和属性。 Every custom attribute in the example should have a common base type - EnhancedAtributte - with an abstract method Check(object[] args) that receives the arguments from the marked method.
示例中的每个自定义属性都应该有一个公共基类型 - 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) { ... }
}
I'd start by verifying that T
is neither sealed nor abstract.我首先要验证
T
既不是密封的也不是抽象的。 That should be sufficient to make sure it's (1) a class;这应该足以确保它是(1)一个类; and (2) capable of being extended.
(2) 可扩展。
Next, iterate over typeof(T).GetProperties()
to find any 'enhanced' properties where CanWrite
is true
and property.GetCustomAttributes<EnhancedAtributte>().Any()
reports true
.接下来,迭代
typeof(T).GetProperties()
以查找CanWrite
为true
且property.GetCustomAttributes<EnhancedAtributte>().Any()
报告true
任何“增强型”属性。 If there aren't any matching properties, you can just instantiate T
directly.如果没有任何匹配的属性,您可以直接实例化
T
Since the property values are validated by the attribute instances themselves, you'll want to cache the attributes somewhere, lest you incur an expensive lookup on each property change.由于属性值由属性实例本身验证,因此您需要将属性缓存在某处,以免在每次属性更改时进行昂贵的查找。 You should aim to generate a class that looks something like this:
你的目标应该是生成一个看起来像这样的类:
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;
}
}
}
A TypeBuilder
can be created from a ModuleBuilder
, which is created from an AssemblyBuilder
. TypeBuilder
可以从ModuleBuilder
创建,而ModuleBuilder
是从AssemblyBuilder
创建的。 For the latter two, you can just keep a static instance around.对于后两者,您可以只保留一个静态实例。
You'll need to use TypeBuilder.DefineField
to define a static field to use as an attribute cache for each property (or use a single EnhancedAttribute[][]
indexed by property).您需要使用
TypeBuilder.DefineField
来定义一个静态字段以用作每个属性的属性缓存(或使用按属性索引的单个EnhancedAttribute[][]
)。 In either case, you'll have to define a class constructor (see TypeBuilder.DefineTypeInitializer
) to initialize the cache.无论哪种情况,您都必须定义一个类构造函数(请参阅
TypeBuilder.DefineTypeInitializer
)来初始化缓存。 You'll have to write the IL yourself using MethodBuilder.GetILGenerator()
.您必须使用
MethodBuilder.GetILGenerator()
自己编写 IL。
For each enhanced property you found, you'll need to define a new property with the same name (see TypeBuilder.DefineProperty
), and emit a separate get
and set
accessor for each (see TypeBuilder.DefineMethod
).对于您找到的每个增强属性,您需要定义一个具有相同名称的新属性(请参阅
TypeBuilder.DefineProperty
),并为每个属性发出单独的get
和set
访问器(请参阅TypeBuilder.DefineMethod
)。 Each accessor will need to be bound to the property, which can be accomplished via PropertyBuilder.SetGetMethod
and SetSetMethod
.每个访问器都需要绑定到属性,这可以通过
PropertyBuilder.SetGetMethod
和SetSetMethod
完成。 You'll also have to make the accessors override the accessors in T
, which you can do via TypeBuilder.DefineMethodOverride
.您还必须使访问器覆盖
T
,您可以通过TypeBuilder.DefineMethodOverride
。 You can see some hints on overriding properties in this question .您可以在此问题中看到有关覆盖属性的一些提示。
The code for the get
accessor override will be simple: you need only delegate to the base property. get
访问器覆盖的代码很简单:您只需要委托给基本属性。 The set
accessor is more complicated, because you'll need to loop over the attribute cache for the property and call each attribute's Check
method. set
访问器更复杂,因为您需要遍历属性的属性缓存并调用每个属性的Check
方法。 Again, you'll need to emit the IL manually, which includes figuring out how to emit a simple for
loop.同样,您需要手动发出 IL,其中包括弄清楚如何发出简单的
for
循环。 Alternatively, since you already know the number of attributes for each property, you could just write a manually-unrolled loop.或者,由于您已经知道每个属性的属性数量,您可以只编写一个手动展开的循环。 Regardless, for each call to
Check
, remember that you'll need to initialize a new object[]
containing into which you must copy your value
parameter.无论如何,对于每次调用
Check
,请记住您需要初始化一个新的object[]
,您必须将value
参数复制到其中。
Once you've declared the attribute cache field(s), the type initializer, the properties, and their accessors, you're essentially finished.一旦您声明了属性缓存字段、类型初始值设定项、属性及其访问器,您基本上就完成了。 You can 'bake' the derived type by calling
CreateType()
on your TypeBuilder
.您可以通过在
TypeBuilder
上调用CreateType()
来“烘焙”派生类型。
I felt like writing some code, so here's a solution that should handle the attribute-based property validation.我想写一些代码,所以这里有一个应该处理基于属性的属性验证的解决方案。 It's not entirely clear to me how the attributes on other methods should work, but this should provide a good starting point nonetheless.
我并不完全清楚其他方法上的属性应该如何工作,但这应该提供一个很好的起点。
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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.