用反射的通用集合转换

时间:2017-02-28 01:49:01

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

我正在使用.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<>),但这不起作用。 typeof(ICollection)对我来说不是一个选项,因为我需要确保目标集合的添加方法为ICollection<T>

2。)第二个问题是转型步骤。

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

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<SomeType>.Add(SomeType)的最佳重载匹配包含一些无效参数。”

我知道并没有像ReadonlyCollections一样为所有ICollection<T>实现Add方法。目前我只想知道这个动态调用是否以及如何运作。

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

1 个答案:

答案 0 :(得分:0)

虽然这个帖子迟到了一年。

的ICollection&LT; T&GT;实施

首先,您最简单的解决方案是在类上显式实现ICollection<T>隐式。由于所有继承/实现ICollection<T>的对象必须具有void Add(T)的方法签名,因此无论是Implicit,Explicit还是两者都取决于实现者。一些开发人员更喜欢将逻辑存储在Explicit实现中,而其他开发人员更喜欢Implicit实现。通常,其中一个实现引用另一个实现源逻辑处理。在特殊情况下,两种实现都允许您使用“通用”(显式)逻辑和“特定”(隐式)逻辑实现。

隐含参考

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<T>类中实现,这实际上是不正确的。如果您查看MSDN documentation,您会看到Add(T)部分中定义了Explicit Interface Implementations方法,但未在隐式实施中定义。

上下文

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

替代方案是对所有可能的ICollection<T>进行一次性反映,并将所有经过验证的对象放入Dictionary<TKey, TValue>KeyedCollection<T>对象中以便稍后提取。这将使您能够验证引用的集合已验证的实现ICollection<T>的对象。