简体   繁体   中英

Generic collection transformation with reflection

I am using .Net Core and have the following problems:

I want to use reflection to transform the properties of an object and store the results in a new copy of that object. This works great for non-collections and arrays, but i am facing problems with generic collections ( ICollection<T> ).

There are two problems:

1.) How to ensure that the runtime type is of type ICollection<any T> . I was able to achieve this for ICollection, but how to check if an object implements the generic interface?

I'd like to do something like this:

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;
}

I Tried typeof(ICollection<>) but that does not work. typeof(ICollection) is not an option for me, since i need to ensure that the target collection has the Add Method of ICollection<T> .

2.) The second problem is about the transformation step.

I tried to use dynamic to ignore static types for the part which adds the transformed items to the new collection:

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
}

The call of the Add method on the dynamic object "x" throws the following exception: "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded match for System.Collections.Generic.List<SomeType>.Add(SomeType) has some invalid arguments."

I'm aware that the Add method isn't implemented for all ICollection<T> like ReadonlyCollections. Currently i just want to know if and how this dynamic call works.

I know that dynamic is unsafe and looks like a hack, but i havent found another solution yet, which does not involve a transformation for each implementation of the collection interfaces.

Albeit over a year late to this Post.

ICollection<T> Implementation

First off your simplest solution would be to implement ICollection<T> implicitly and explicitly on the classes. Since all objects that inherit/implement ICollection<T> MUST have a method signature for void Add(T) , it is up to the implementor whether it is Implicit, Explicit, or both. Some developer prefer storing the logic in the Explicit implementation and other prefer the Implicit implementation. Generally, have one of the implementations refer to the other for the source logic handling. In special circumstances, having both implementations allows you to have a "Generic" (Explicit) logic and a "Specific" (Implicit) logic implementation.

Implicit Reference

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
}

Explicit Reference

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 Reflection

As far as your dynamic question. Think of dynamic as a loosely-typed object, similar to how JavaScript allows you to flip a variables underlying data-type as you wish, through scoping and other leveraging strategies. Either way, Reflection generally requires the resulting object to be strongly-typed, when coding.

As far as the Add(T) method not being implemented in all ICollection<T> classes, that is factually incorrect. If you look at the MSDN documentation , you will see that the Add(T) method is defined in the Explicit Interface Implementations section, it is not defined in the Implicit Implementation.


Context

The one point of this Question that is missing is the contextual need to do all this recursive reflection of types, properties, and member existence. In order to minimize the costs of reflecting a library, over and over; your methods needs to be designed with the Assumption that certain criteria have been met and work forward from that assumption. In the methods you provided, you are presuming no Assumptions going into either. This will add unnecessary logic load into your implementation with the repeated acquisition of the object(s) meta-data.

An Alternative would be to do a one-time reflection of all possible ICollection<T> and place all validated objects into a Dictionary<TKey, TValue> or KeyedCollection<T> object for extraction at a later time. This will provide you the ability to have validated the referenced collection ONLY has validated objects that implement ICollection<T> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM