【发布时间】:2015-08-12 19:15:27
【问题描述】:
我有这个帮助方法,旨在将集合项从一个集合对象实例传输到另一个集合对象实例。它有效,但我最近遇到了一个问题,即特定集合在不同点实现IEnumerable<T>;。一级为IEnumerable<KeyValuePair<TKey, TValue>>,另一级为IEnumerable<TValue>。在我下面的代码中,secondaryCollection 的声明导致它使用 IEnumerable<TValue> 实例类型,而 collectionType 声明发现它是基本的 ICollection<KeyValuePair<TKey, TValue>> 类型,因此我可以调用 Add() 和 Remove()。尽管Add() 和Remove() 方法调用失败,但这种类型不匹配。我想如果我能弄清楚如何将secondaryCollection 声明为IEnumerable<object> 类型,其中“对象”的类型为KeyValuePair<TKey, TValue> 而不仅仅是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
}
}
}
编辑:
也许只是为了澄清我需要帮助的内容...我有一个自定义集合,该集合用于由使用反射的方法评估的类中。此集合两次实现 IEnumerable...IEnumerable<TValue> 和 IEnumerable<KeyValuePair<TKey, TValue>>。而不是这个...
IEnumerable<object> secondaryCollection = ((IEnumerable)secondaryCollectionObject).Cast<object>();
由于Cast<T>() 操作,最终使用IEnumerable<TValue>,我需要secondaryCollection 使用IEnumerable<KeyValuePair<TKey, TValue>> 的东西。并且它无法知道该集合最初使用了两种实现。从这一行开始:
Type collectionType = primaryMember.FieldType.GetBaseTypes().FirstOrDefault(t => t.IsGenericType && t.GetGenericArguments().Length == 1 && t == genericType.MakeGenericType(t.GetGenericArguments()));
确实识别了正确的类型,我原本以为可以使用,但我不确定如何使用。
【问题讨论】:
-
你为什么不直接做
public void MoveItems<T>(ICollection<T> source, ICollection<T> target) { /* code to remove all items from source and add to target without reflection */ }? -
我不能,因为这只是一个流程的一小部分,旨在将较大的对象(消息)拆分为多个较小的对象,跨流程边界发送。这整个过程是通过反射处理的,因此它不必知道细节(或者即使 Message 对象有一个集合,更不用说它的集合类型了)。这使我可以将不同类型的 Message 对象用于不同的目的。
-
集合可能实现了多个 IEnumerable
,但只有一个 ICollection 。
标签: c# .net reflection