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