【问题标题】:How do I set value of field that is a generic interface through reflection?如何通过反射设置作为通用接口的字段的值?
【发布时间】: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&lt;object, object&gt;,则可以直接调用它。

标签: c# .net reflection


【解决方案1】:

你的 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

【讨论】:

  • 啊!我没有意识到我需要将编译后的 lambda 视为一个对象。很有意思。我需要更多地了解 lambda 表达式。不过,这对我有用。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2019-04-22
  • 1970-01-01
  • 2014-10-07
  • 2010-09-14
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 2016-01-08
相关资源
最近更新 更多