【问题标题】:Generic collection transformation with reflection带反射的通用集合转换
【发布时间】:2017-07-18 20:01:54
【问题描述】:

我正在使用.Net Core,遇到以下问题:

我想使用反射来转换对象的属性并将结果存储在该对象的新副本中。这对非集合和数组很有用,但我遇到了泛型集合的问题 (ICollection<T>)。

有两个问题:

1.) 如何确保运行时类型为ICollection<any T> 类型。我能够为 ICollection 实现这一点,但是如何检查对象是否实现了泛型接口?

我想做这样的事情:

public object Transform(object objectToTransform) {
    var type = objectToTransform.GetType();
    var obj = Activator.CreateInstance(type);
    foreach (var propertyInfo in type.GetRuntimeProperties()) {
        ...
        if(typeof(ICollection<???>).GetTypeInfo().IsAssignableFrom(propertyInfo.PropertyType.GetTypeInfo())) {
            // transform all items and store them in a new collection of the same runtime type
        }
        ...
    }
    return obj;
}

我尝试了typeof(ICollection&lt;&gt;),但这不起作用。 typeof(ICollection) 对我来说不是一个选项,因为我需要确保目标集合具有 ICollection&lt;T&gt; 的添加方法。

2.) 第二个问题是关于转换步骤的。

我尝试使用动态来忽略将转换后的项目添加到新集合的部分的静态类型:

var collection = (IEnumerable)propertyInfo.GetMethod.Invoke(objectToTransform, null);
dynamic x = Activator.CreateInstance(collection.GetType());
foreach (var item in collection) {
    x.Add(Transform(item)); // Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
}

对动态对象“x”调用 Add 方法会引发以下异常:“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:System.Collections.Generic.List&lt;SomeType&gt;.Add(SomeType) 的最佳重载匹配有一些无效参数。”

我知道 Add 方法并未针对所有 ICollection&lt;T&gt; 实现,例如 ReadonlyCollections。目前我只想知道这个动态调用是否以及如何工作。

我知道动态是不安全的并且看起来像一个黑客,但我还没有找到另一个解决方案,它不涉及对集合接口的每个实现的转换。

【问题讨论】:

    标签: c# generics dynamic reflection .net-core


    【解决方案1】:

    尽管这篇文章晚了一年多。

    ICollection 实现

    首先,您最简单的解决方案是在类上隐式实现ICollection&lt;T&gt;。由于继承/实现ICollection&lt;T&gt; 的所有对象都必须具有void Add(T) 的方法签名,因此它是隐式、显式还是两者都取决于实现者。一些开发人员更喜欢将逻辑存储在显式实现中,而其他开发人员更喜欢隐式实现。通常,让其中一个实现参考另一个实现源逻辑处理。在特殊情况下,同时拥有这两种实现可以让您拥有“通用”(显式)逻辑和“特定”(隐式)逻辑实现。

    隐式引用

    public class AnyCollection:ICollection&lt;object&gt;{
    
        public void Add(object val){
            // some code to add the object  
        }
    
        #region Explicit ICollection<T> implementation
    
        void ICollection<object>.Add(object val){
            // Call the instance method
            this.Add(val);  
        }
        #endregion
    }
    

    显式引用

    public class AnyCollection:ICollection&lt;object&gt;{
    
        public void Add(object val){
            (this as ICollection<object>).Add(val);             
        }
    
        #region Explicit ICollection<T> implementation
    
        void ICollection<object>.Add(object val){
            // some code to add the object  
        }
        #endregion
    }
    

    动态反射

    就您的dynamic 问题而言。将dynamic 视为一个松散类型的对象,类似于 JavaScript 允许您通过范围和其他利用策略根据需要翻转底层数据类型的变量的方式。无论哪种方式,Reflection 在编码时通常要求生成的对象是强类型的。

    至于Add(T) 方法并未在所有ICollection&lt;T&gt; 类中实现,这实际上是不正确的。如果您查看MSDN documentation,您会看到Add(T) 方法是在Explicit Interface Implementations 部分中定义的,它没有在隐式实现中定义。


    上下文

    这个问题缺少的一点是上下文需要对类型、属性和成员存在进行所有这些递归反射。为了最大限度地减少反映图书馆的成本,一遍又一遍;您的方法需要在满足某些标准的假设下进行设计,并从该假设出发。在您提供的方法中,您假设没有任何假设。通过重复获取对象元数据,这将在您的实现中添加不必要的逻辑负载。

    另一种方法是对所有可能的ICollection&lt;T&gt; 进行一次反射,并将所有经过验证的对象放入Dictionary&lt;TKey, TValue&gt;KeyedCollection&lt;T&gt; 对象中以便以后提取。这将使您能够验证所引用的集合已验证实现ICollection&lt;T&gt; 的对象。

    【讨论】:

      猜你喜欢
      • 2010-12-15
      • 1970-01-01
      • 2010-09-11
      • 2011-02-08
      • 2011-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-19
      相关资源
      最近更新 更多