如何为类型为对象的 属性 setter 创建委托
How to create a delegate for a property setter with its type as object
我正在寻找一种在不知道 属性 类型的情况下为 属性 的 setter 创建委托的方法,而不是让 Action<TClass, TType>
,我要Action<TClass, object>
。将调用 Action<TClass, object>
的代码总是将正确的基础类型装箱为 object
。目的是稍后缓存这些委托。
请考虑以下示例。
public class MyClass<T>
{
public T MyProperty { get; set; }
}
我可以设置MyProperty
如下。
var myClass = new MyClass<int>();
var property = typeof(MyClass<int>).GetProperty("MyProperty");
object intAsObject = 2;
//Option 1. This works, but is slow
property.SetValue(myClass, intAsObject);
//Option 2. This works, but I need to know the type of MyProperty at compile time.
var setter = (Action<MyClass<int>, int>)property.SetMethod.CreateDelegate(typeof(Action<MyClass<int>, int>));
setter(myClass, 5);
//Option 3. This does not work. It throws ArgumentException. Is it possible to achieve something like this?!
var objSetter = (Action<MyClass<int>, object>)property.SetMethod.CreateDelegate(typeof(Action<MyClass<int>, object>));
objSetter(myClass, intAsObject);
如何实现选项#3?抛出 ArgumentException
的消息如下:
无法绑定到目标方法,因为它的签名或安全透明度与委托类型不兼容。
此外,是否可以让它适用于任何 T
,即 int
、double
、string
、object
等?
我找到了有趣的答案,例如以下内容,但找不到实现我想要的方法。
Creating a property setter delegate
非常感谢您。
感谢 Iliar Turdushev 的评论,他指出了线程 here, and thanks also to this blog post here 我能够想出以下内容。
public static Delegate CreateSetter(PropertyInfo propertyInfo)
{
ParameterExpression instance = Expression.Parameter(propertyInfo.ReflectedType, "instance");
ParameterExpression propertyValue = Expression.Parameter(propertyInfo.PropertyType, "propertyValue");
var body = Expression.Assign(Expression.Property(instance, propertyInfo.Name), propertyValue);
return Expression.Lambda(body, instance, propertyValue).Compile();
}
然后就可以这样使用了(还是考虑我问题中的例子class)。
//The types are known for this example. But in my real scenario only an instance of PropertyInfo will be available.
var propertyInfo = typeof(MyClass<int>).GetProperty(nameof(MyClass<int>.MyProperty));
var deleg = CreateSetter(propertyInfo);
deleg.DynamicInvoke(myClass, 10);
//Prints 10 as expected
Console.WriteLine(myClass.MyProperty);
编辑: 刚发现下面的也可以:
public static Action<object, object> CreateSetter(PropertyInfo propertyInfo)
{
ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
UnaryExpression instanceExpresion = Expression.Convert(instance, propertyInfo.DeclaringType);
ParameterExpression propertyValue = Expression.Parameter(typeof(object), "propertyValue");
UnaryExpression propertyValueExpression = Expression.Convert(propertyValue, propertyInfo.PropertyType);
var body = Expression.Assign(Expression.Property(instanceExpresion, propertyInfo.Name), propertyValueExpression);
return Expression.Lambda<Action<object, object>>(body, instance, propertyValue).Compile();
}
并以这种方式使用。
var action = CreateSetter(propertyInfo);
action.Invoke(myClass, 10);
我正在寻找一种在不知道 属性 类型的情况下为 属性 的 setter 创建委托的方法,而不是让 Action<TClass, TType>
,我要Action<TClass, object>
。将调用 Action<TClass, object>
的代码总是将正确的基础类型装箱为 object
。目的是稍后缓存这些委托。
请考虑以下示例。
public class MyClass<T>
{
public T MyProperty { get; set; }
}
我可以设置MyProperty
如下。
var myClass = new MyClass<int>();
var property = typeof(MyClass<int>).GetProperty("MyProperty");
object intAsObject = 2;
//Option 1. This works, but is slow
property.SetValue(myClass, intAsObject);
//Option 2. This works, but I need to know the type of MyProperty at compile time.
var setter = (Action<MyClass<int>, int>)property.SetMethod.CreateDelegate(typeof(Action<MyClass<int>, int>));
setter(myClass, 5);
//Option 3. This does not work. It throws ArgumentException. Is it possible to achieve something like this?!
var objSetter = (Action<MyClass<int>, object>)property.SetMethod.CreateDelegate(typeof(Action<MyClass<int>, object>));
objSetter(myClass, intAsObject);
如何实现选项#3?抛出 ArgumentException
的消息如下:
无法绑定到目标方法,因为它的签名或安全透明度与委托类型不兼容。
此外,是否可以让它适用于任何 T
,即 int
、double
、string
、object
等?
我找到了有趣的答案,例如以下内容,但找不到实现我想要的方法。 Creating a property setter delegate
非常感谢您。
感谢 Iliar Turdushev 的评论,他指出了线程 here, and thanks also to this blog post here 我能够想出以下内容。
public static Delegate CreateSetter(PropertyInfo propertyInfo)
{
ParameterExpression instance = Expression.Parameter(propertyInfo.ReflectedType, "instance");
ParameterExpression propertyValue = Expression.Parameter(propertyInfo.PropertyType, "propertyValue");
var body = Expression.Assign(Expression.Property(instance, propertyInfo.Name), propertyValue);
return Expression.Lambda(body, instance, propertyValue).Compile();
}
然后就可以这样使用了(还是考虑我问题中的例子class)。
//The types are known for this example. But in my real scenario only an instance of PropertyInfo will be available.
var propertyInfo = typeof(MyClass<int>).GetProperty(nameof(MyClass<int>.MyProperty));
var deleg = CreateSetter(propertyInfo);
deleg.DynamicInvoke(myClass, 10);
//Prints 10 as expected
Console.WriteLine(myClass.MyProperty);
编辑: 刚发现下面的也可以:
public static Action<object, object> CreateSetter(PropertyInfo propertyInfo)
{
ParameterExpression instance = Expression.Parameter(typeof(object), "instance");
UnaryExpression instanceExpresion = Expression.Convert(instance, propertyInfo.DeclaringType);
ParameterExpression propertyValue = Expression.Parameter(typeof(object), "propertyValue");
UnaryExpression propertyValueExpression = Expression.Convert(propertyValue, propertyInfo.PropertyType);
var body = Expression.Assign(Expression.Property(instanceExpresion, propertyInfo.Name), propertyValueExpression);
return Expression.Lambda<Action<object, object>>(body, instance, propertyValue).Compile();
}
并以这种方式使用。
var action = CreateSetter(propertyInfo);
action.Invoke(myClass, 10);