C# 使用两个IEnumerable传输用于收集的收集项目<;T>;带反射的实现

C# 使用两个IEnumerable传输用于收集的收集项目<;T>;带反射的实现,c#,.net,reflection,C#,.net,Reflection,我有一个helper方法,用于将集合项从一个集合对象实例转移到另一个集合对象实例。它可以工作,但我最近遇到了一个问题,即特定集合在不同的点实现IEnumerable。一个级别为IEnumerable,另一个级别为IEnumerable。在下面的代码中,secondaryCollection的声明使其使用IEnumerable实例类型,而collectionType声明将其作为基本ICollection类型,以便调用Add()和Remove()。尽管Add()和Remove()方法调用失败,但此类

我有一个helper方法,用于将集合项从一个集合对象实例转移到另一个集合对象实例。它可以工作,但我最近遇到了一个问题,即特定集合在不同的点实现
IEnumerable。一个级别为
IEnumerable
,另一个级别为
IEnumerable
。在下面的代码中,
secondaryCollection
的声明使其使用
IEnumerable
实例类型,而collectionType声明将其作为基本
ICollection
类型,以便调用
Add()
Remove()
。尽管
Add()
Remove()
方法调用失败,但此类型不匹配。我想如果我能弄清楚如何将
secondaryCollection
声明为类型
IEnumerable
,其中“object”的类型是
KeyValuePair
,而不仅仅是类型
TValue
,那么这应该在没有类型不匹配异常的情况下工作(它实际上是
Add()
Remove()的参数异常)
方法)。问题是这些都是在反射中完成的,类型未知。我该怎么做

以下是当前的方法代码:

public void MergeCollection(FieldInfo primaryMember, object primaryObject, FieldInfo secondaryMember, object secondaryObject)
    {
        if (primaryMember == null)
            throw new ArgumentNullException("primaryMember");

        if (primaryObject == null)
            throw new ArgumentNullException("primaryObject");

        if (secondaryMember == null)
            throw new ArgumentNullException("secondaryMember");

        if (secondaryObject == null)
            throw new ArgumentNullException("secondaryObject");

        //Get the collection type and validate
        Type genericType = typeof(ICollection<>);

        Type collectionType = primaryMember.FieldType.GetBaseTypes().FirstOrDefault(t => t.IsGenericType && t.GetGenericArguments().Length == 1 && t == genericType.MakeGenericType(t.GetGenericArguments()));

        if (!collectionType.IsAssignableFrom(secondaryMember.FieldType))
            throw new InvalidOperationException("Primary and secondary collection types do not match.");

        Type collectionParamType = collectionType.GetGenericArguments()[0];


        //Get the collection invocable methods
        MethodInfo add = collectionType.GetMethod("Add", new Type[] { collectionParamType });
        MethodInfo remove = collectionType.GetMethod("Remove", new Type[] { collectionParamType });

        //Declare the collections
        object primaryCollectionObject = primaryMember.GetValue(primaryObject);
        object secondaryCollectionObject = secondaryMember.GetValue(secondaryObject);

        Type genericEnumerableType = typeof(IEnumerable<>);
        Type enumerableType = primaryMember.FieldType.GetBaseTypes().FirstOrDefault(t => t.IsGenericType && t.GetGenericArguments().Length == 1 && t == genericEnumerableType.MakeGenericType(t.GetGenericArguments()));

        IEnumerable<object> secondaryCollection = ((IEnumerable)secondaryCollectionObject).Cast<object>();

        //Transfer the items
        int noItems = secondaryCollection.Count();
        // int noItems = (int)count.GetValue(secondaryCollectionObject);
        for (int i = 0; i < noItems; i++)
        {
            try
            {
                add.Invoke(primaryCollectionObject, new object[] { secondaryCollection.ElementAt(0) });
                remove.Invoke(secondaryCollectionObject, new object[] { secondaryCollection.ElementAt(0) });
            }
            catch (ArgumentException ex)
            {
                //The argument exception can be captured here
            }
        }
    }

确实确定了正确的类型,我最初认为它可以使用,但我不确定如何使用。

正如您正确地说的,问题在于Cast方法。因为您没有在Cast方法中传递正确的类型,所以IEnumerable出现了错误。解决方案不是使用类型“object”调用Cast方法,而是使用适当的类型。由于在编译时没有适当的类型信息,您可能需要使用反射调用Cast,如下所示:

var castMethod = typeof(Enumerable).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public);
var castGenericMethod = castMethod.MakeGenericMethod(new Type[] {  collectionParamType});
secondaryCollection = castGenericMethod.Invoke(null, new object[] {secondaryCollectionObject})
注意:我刚刚在iPad中键入了上述代码,因此可能存在一些语法问题。

这可能会起作用(只需将集合作为源和目标传递):

publicstaticvoidmoveitems(动态源、动态目标){
MoveItemSiml(源、目标);
}
公共静态void MoveItemsImpl(ICollection源、动态目标){
foreach(源中的T项)
目标。添加(项目);
source.Clear();//可选
}

有点老套,但值得一试:)

在我准备答案时,我刚刚看到佐塔带来了一个类似的想法。这是我的。最简单的方法是用“纯”C#泛型方法实现主实现,然后通过反射调用它。现在的问题是如何通过反射调用泛型方法。关于这一点,有几个SO项目,到目前为止,最简单的方法是使用动态特性。下面是一个示例解决方案,包括动态或纯反射方法(跳过验证)和一个简单测试:

static class Helper
{
    public static void Merge<T>(ICollection<T> source, ICollection<T> target)
    {
        foreach (var item in source) target.Add(item);
        source.Clear();
    }

    #region Using dynamic

    public static void MergeCollection(FieldInfo sourceMember, object sourceObject, FieldInfo targetMember, object targetObject)
    {
        var sourceCollection = sourceMember.GetValue(sourceObject);
        var targetCollection = targetMember.GetValue(targetObject);
        Merge((dynamic)sourceCollection, (dynamic)targetCollection);
    }

    #endregion

    #region Using reflection only

    public static void MergeCollection2(FieldInfo sourceMember, object sourceObject, FieldInfo targetMember, object targetObject)
    {
        var collectionType = targetMember.FieldType.GetInterfaces().Single(
            t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>)
        );
        var itemType = collectionType.GetGenericArguments()[0];
        var mergeMethod = MergeMethodInfo.MakeGenericMethod(itemType);
        var sourceCollection = sourceMember.GetValue(sourceObject);
        var targetCollection = targetMember.GetValue(targetObject);
        mergeMethod.Invoke(null, new[] { sourceCollection, targetCollection });
    }

    private static readonly MethodInfo MergeMethodInfo = GetGenericMethodDefinition(
        (ICollection<object> source, ICollection<object> target) => Merge(source, target)
    );

    private static MethodInfo GetGenericMethodDefinition<T1, T2>(Expression<Action<T1, T2>> e)
    {
        return ((MethodCallExpression)e.Body).Method.GetGenericMethodDefinition();
    }

    #endregion

    #region Test

    class MyCollection1<TKey, TValue> : Dictionary<TKey, TValue>, IEnumerable<TValue>
    {
        IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() { return Values.GetEnumerator(); }
    }

    class MyCollection2<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IEnumerable<TValue>
    {
        IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
        {
            IEnumerable<KeyValuePair<TKey, TValue>> e = this;
            return e.Select(item => item.Value).GetEnumerator();
        }
    }

    class MyClass1
    {
        public MyCollection1<int, string> Items1 = new MyCollection1<int, string>();
    }

    class MyClass2
    {
        public MyCollection2<int, string> Items2 = new MyCollection2<int, string>();
    }

    private static FieldInfo GetField<T, V>(Expression<Func<T, V>> e)
    {
        return (FieldInfo)((MemberExpression)e.Body).Member;
    }

    public static void Test()
    {
        var source = new MyClass1();
        for (int i = 0; i < 10; i++) source.Items1.Add(i + 1, new string((char)('A' + i), 1));
        var target = new MyClass2();
        var sourceField = GetField((MyClass1 c) => c.Items1);
        var targetField = GetField((MyClass2 c) => c.Items2);
        // Merge source into target using dynamic approach
        MergeCollection(sourceField, source, targetField, target);
        // Merge target back into source using reflection approach
        MergeCollection2(targetField, target, sourceField, source);
    }

    #endregion
}
静态类助手
{
公共静态无效合并(ICollection源、ICollection目标)
{
foreach(源中的var项)target.Add(项);
source.Clear();
}
#区域使用动态
公共静态void合并集合(FieldInfo sourceMember、object sourceObject、FieldInfo targetMember、object targetObject)
{
var sourceCollection=sourceMember.GetValue(sourceObject);
var targetCollection=targetMember.GetValue(targetObject);
合并((动态)sourceCollection,(动态)targetCollection);
}
#端区
#仅使用反射的区域
公共静态无效合并集合2(FieldInfo sourceMember、object sourceObject、FieldInfo targetMember、object targetObject)
{
var collectionType=targetMember.FieldType.GetInterfaces().Single(
t=>t.IsGenericType&&t.GetGenericTypeDefinition()==typeof(ICollection)
);
var itemType=collectionType.GetGenericArguments()[0];
var mergeMethod=MergeMethodInfo.MakeGenericMethod(itemType);
var sourceCollection=sourceMember.GetValue(sourceObject);
var targetCollection=targetMember.GetValue(targetObject);
调用(null,new[]{sourceCollection,targetCollection});
}
私有静态只读MethodInfo MergeMethodInfo=GetGenericMethodDefinition(
(ICollection源,ICollection目标)=>合并(源,目标)
);
私有静态方法信息GetGenericMethodDefinition(表达式e)
{
return((MethodCallExpression)e.Body.Method.GetGenericMethodDefinition();
}
#端区
#区域测试
MyCollection1类:字典,IEnumerable
{
IEnumerator IEnumerable.GetEnumerator(){返回值。GetEnumerator();}
}
MyCollection2类:列表,IEnumerable
{
IEnumerator IEnumerable.GetEnumerator()
{
IEnumerable e=这个;
返回e.Select(item=>item.Value).GetEnumerator();
}
}
类别MyClass1
{
public MyCollection1 Items1=新的MyCollection1();
}
类别MyClass2
{
public MyCollection2 Items2=新的MyCollection2();
}
私有静态FieldInfo GetField(表达式e)
{
return(FieldInfo)((MemberExpression)e.Body.Member;
}
公共静态无效测试()
{
var source=new MyClass1();
对于(inti=0;i<10;i++)source.Items1.Add(i+1,新字符串((char)('A'+i),1));
var target=new MyClass2();
var sourceField=GetField((myclass1c)=>c.Items1);
var targetField=GetField((MyClass2 c)=>c.Items2);
//使用动态方法将源合并到目标
合并集合(sourceField、source、targetField、target);
//使用反射方法将目标合并回源
合并集合2(targetField、target、sourceField、source);
}
#端区
}

为什么不执行
public void MoveItems(ICollection source,ICollection target){/*从源代码中删除所有项并添加到目标而不进行反射*/}
?我不能,因为这只是拆分较大对象(Messag)过程的一小部分
var castMethod = typeof(Enumerable).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public);
var castGenericMethod = castMethod.MakeGenericMethod(new Type[] {  collectionParamType});
secondaryCollection = castGenericMethod.Invoke(null, new object[] {secondaryCollectionObject})
public static void MoveItems(dynamic source, dynamic target) {
    MoveItemsImpl(source, target);
}

public static void MoveItemsImpl<T>(ICollection<T> source, dynamic target) {
    foreach(T item in source)
        target.Add(item);
    source.Clear();//optional
}
static class Helper
{
    public static void Merge<T>(ICollection<T> source, ICollection<T> target)
    {
        foreach (var item in source) target.Add(item);
        source.Clear();
    }

    #region Using dynamic

    public static void MergeCollection(FieldInfo sourceMember, object sourceObject, FieldInfo targetMember, object targetObject)
    {
        var sourceCollection = sourceMember.GetValue(sourceObject);
        var targetCollection = targetMember.GetValue(targetObject);
        Merge((dynamic)sourceCollection, (dynamic)targetCollection);
    }

    #endregion

    #region Using reflection only

    public static void MergeCollection2(FieldInfo sourceMember, object sourceObject, FieldInfo targetMember, object targetObject)
    {
        var collectionType = targetMember.FieldType.GetInterfaces().Single(
            t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>)
        );
        var itemType = collectionType.GetGenericArguments()[0];
        var mergeMethod = MergeMethodInfo.MakeGenericMethod(itemType);
        var sourceCollection = sourceMember.GetValue(sourceObject);
        var targetCollection = targetMember.GetValue(targetObject);
        mergeMethod.Invoke(null, new[] { sourceCollection, targetCollection });
    }

    private static readonly MethodInfo MergeMethodInfo = GetGenericMethodDefinition(
        (ICollection<object> source, ICollection<object> target) => Merge(source, target)
    );

    private static MethodInfo GetGenericMethodDefinition<T1, T2>(Expression<Action<T1, T2>> e)
    {
        return ((MethodCallExpression)e.Body).Method.GetGenericMethodDefinition();
    }

    #endregion

    #region Test

    class MyCollection1<TKey, TValue> : Dictionary<TKey, TValue>, IEnumerable<TValue>
    {
        IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() { return Values.GetEnumerator(); }
    }

    class MyCollection2<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IEnumerable<TValue>
    {
        IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
        {
            IEnumerable<KeyValuePair<TKey, TValue>> e = this;
            return e.Select(item => item.Value).GetEnumerator();
        }
    }

    class MyClass1
    {
        public MyCollection1<int, string> Items1 = new MyCollection1<int, string>();
    }

    class MyClass2
    {
        public MyCollection2<int, string> Items2 = new MyCollection2<int, string>();
    }

    private static FieldInfo GetField<T, V>(Expression<Func<T, V>> e)
    {
        return (FieldInfo)((MemberExpression)e.Body).Member;
    }

    public static void Test()
    {
        var source = new MyClass1();
        for (int i = 0; i < 10; i++) source.Items1.Add(i + 1, new string((char)('A' + i), 1));
        var target = new MyClass2();
        var sourceField = GetField((MyClass1 c) => c.Items1);
        var targetField = GetField((MyClass2 c) => c.Items2);
        // Merge source into target using dynamic approach
        MergeCollection(sourceField, source, targetField, target);
        // Merge target back into source using reflection approach
        MergeCollection2(targetField, target, sourceField, source);
    }

    #endregion
}