如何通过反射设置作为通用接口的字段的值?

时间:2019-02-12 01:03:53

标签: c# .net reflection

我希望能够使用反射来获得IList类型的类的成员,并且我想为其创建一个委托,以便为反射花更少的钱。所有这些使我可以通过传入实例和新的List来调用委托。每当我尝试这样做时,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 });
    }

}

对我来说,预期结果是能够将“ setter”缓存到该属性,并支持设置任何类型的值,包括泛型和接口。 我当前的结果是,在有泛型的情况下,任何类型的设置值都可以使用...所以我必须做错了什么。

1 个答案:

答案 0 :(得分:0)

您的lambda呼叫有误。您必须在传递目标以及参数的lambda对象上调用Invoke方法。

如果您解决了电话问题,则会获得理想的结果:

testObject.Dump();

var list = new List<int>();
lambdaInvokeMethod.Invoke(lambda, new object[] { testObject, list });

testObject.Dump();

Dumping object(MyObject)
 MyField  : null
Dumping object(MyObject)
 MyField  : []

请参阅:https://dotnetfiddle.net/FZN3iX