【发布时间】:2019-07-05 14:04:06
【问题描述】:
我希望能够使用反射来获得一个类的成员,例如 IList 类型,并且我想为它创建一个委托,以便我为反射支付更少的费用。所有这一切,以至于后来,我可以通过传入我的实例和一个新列表来调用委托。每当我尝试这个时,C# 都会给我无效的强制转换异常。我错过了一些关于泛型的傻事吗?
我尝试过使用没有泛型的接口字段,这些字段似乎工作得很好。我似乎只在使用泛型时遇到这个问题。我还尝试使用 Convert.ChangeType 将我的输入转换为所需的类型,但我收到有关 List 未实现 IConvertible 的错误。如果我可以使用反射从具体的 List 设置 IList 类型的值,我会很高兴:)
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
public class MyObject
{
public IList<int> MyField;
}
public class ReflectionPerfTesting
{
public void Run()
{
var testObject = new MyObject();
var field = testObject.GetType().GetField("MyField", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
InvokeUsingReflection(field, testObject);
}
private void InvokeUsingReflection(FieldInfo fieldInfo, object testObject)
{
var fieldType = fieldInfo.FieldType;
var sourceType = fieldInfo.DeclaringType;
// ZAS: create value parameter
var valueParam = Expression.Parameter(fieldType);
// ZAS: create targetParameter
var sourceParam = Expression.Parameter(sourceType);
var field = Expression.Field(sourceParam, fieldInfo);
var returnExpression = (Expression)Expression.Assign(field, valueParam);
if (!fieldType.IsClass)
{
returnExpression = Expression.Convert(returnExpression, fieldType);
}
// ZAS: create generic delegate from definition
var delegateType = typeof(Action<,>);
var genericType = delegateType.MakeGenericType(sourceType, fieldType);
// ZAS: Create lambda for faster access to setter using reflection. This is meant to be cached!
var lambdaExpression = Expression.Lambda(genericType, returnExpression, sourceParam, valueParam);
var lambda = lambdaExpression.Compile();
var lambdaInvokeMethod = lambda.GetType().GetMethod("Invoke");
var list = new List<int>();
lambdaInvokeMethod.Invoke(testObject, new object[] { list });
}
}
我的预期结果是能够将“设置器”缓存到属性并支持设置任何类型的值,包括泛型和接口。 我目前的结果是设置任何类型的值都可以工作,除非有泛型......所以我一定做错了。
【问题讨论】:
-
如果您必须调用使用反射创建的 lambda,您可能一无所获。如果您将委托创建为
Action<object, object>,则可以直接调用它。
标签: c# .net reflection