通过反射动态修改 IEnumerable 属性

Posted

技术标签:

【中文标题】通过反射动态修改 IEnumerable 属性【英文标题】:Dynamically modifying an IEnumerable property via reflection 【发布时间】:2020-12-14 16:50:31 【问题描述】:

我有各种各样的类,它们具有实现IEnumerable 的各种属性(例如IEnumerable<string>IEnumerable<bool>IEnumerable<enum> 等)。我正在尝试编写一些代码来过滤这些属性的值(例如,如果值为 "one", "two", "three" ,我可能想过滤.Contains("t") 的位置)。

这是我所拥有的精髓:

class MyObject

    public IEnumerable<string> stringProp  get; set;  = new[]  "one", "two", "three" ;
    public IEnumerable<bool> boolProp  get; set;  = new[]  true, false, true ;
    public IEnumerable<int> intProp  get; set;  = new[]  1, 2, 3 ;


public static void Main(string[] args)

    MyObject obj = new MyObject();
    
    foreach (PropertyInfo prop in typeof(MyObject).GetProperties())
                   
        prop.SetValue(obj, (prop.GetValue(obj) as IEnumerable<dynamic>).Where(val => val != null));
    

问题是,当我尝试将值设置回对象 (property.SetValue) 时,会引发错误,因为新值是 IEnumerable&lt;object&gt;

Object of type 'System.Linq.Enumerable+WhereArrayIterator`1[System.Object]' cannot be converted to type 'System.Collections.Generic.IEnumerable`1[System.String]'

我尝试过Convert.ChangeType,但这不起作用,因为IEnumerable 没有实现IConvertible

我该如何做到这一点?为什么 LINQ Where 查询将 IEnumerable&lt;dynamic&gt; 更改为 IEnumerable&lt;object&gt;

【问题讨论】:

能否请您添加错误和minimal reproducible example ? 另外,转换类型的不是Where,而是compiler IEnumerable 是 IEnumerable,编译时检查较少。您必须切换到 TItemType 或使用非泛型 IEnumerable 实现。无论哪种方式,您都可能需要重新考虑代码。另外,我不清楚您实际上想要完成什么。 100 次中有 99 次,使用 dynamic 会为它解决的每个问题多产生 2 个问题。 我添加了一个最小的可重现示例 【参考方案1】:

我理解正确吗?你在寻找这样的东西吗?

var obj = new MyObject();

foreach (var prop in typeof(MyObject).GetProperties())

    //assumming all things are IEnumerable<something>
    var type = prop.PropertyType.GenericTypeArguments[0];

    //We can't "instantiate" something as ephemeral as an IEnumerable,
    //so we need something more concrete like a List
    //There might be other ways to filter - this seemed to be the easiest
    var listType = typeof(List<>).MakeGenericType(type);
    var instance = (IList)Activator.CreateInstance(listType);
    
    var currentEnum = (IEnumerable)prop.GetValue(obj);
    foreach (var item in currentEnum)
    
         if (item != default) // != null would be silly for booleans and ints
         
             instance.Add(item);
         
    

    prop.SetValue(obj, instance);

总结:泛型和动态关键字通常不会以这种方式混合使用 - 使用动态泛型参数是没有意义的。将动态视为实际上意味着“对象”的东西,但也可以让你写任何你喜欢的东西。当然,IEnumerable 可能更适合作为 IEnumerable。对于具有多个参数的泛型,最好使用对象,甚至更好的特定类。

【讨论】:

啊,是的!这样可行!我不知道MakeGenericTypeActivator.CreateInstance 根据您实际想要在代码中执行的操作,您可能还需要查看表达式和函数。也许你可以在没有这些讨厌的反射的情况下做到这一点:)

以上是关于通过反射动态修改 IEnumerable 属性的主要内容,如果未能解决你的问题,请参考以下文章

Java的反射

反射:获取属性值的不同方式

JVM的反射实现

Java 反射机制

OC的反射机制

反射学习