I have this helper method that is designed to transfer collection items from one collection object instance to another. It works, but I have recently ran into an issue where a particular collection implements at different points IEnumerable<T>;
. At one level as IEnumerable<KeyValuePair<TKey, TValue>>
and at another IEnumerable<TValue>
. In my code below, the declaration of secondaryCollection
causes it to use the IEnumerable<TValue>
instance type versus the collectionType declaration finds it as the base ICollection<KeyValuePair<TKey, TValue>>
type so that I can invoke the Add()
and Remove()
. With this type mismatch though the Add()
and Remove()
method invocations fail. I think if I can figure out how to declare secondaryCollection
as type IEnumerable<object>
where 'object' is of type KeyValuePair<TKey, TValue>
and not of just the type TValue
that this should work without the type mismatch exception (it's actually an argument exception for the Add()
, Remove()
methods). The problem is this is all done in reflection and the types are unknown. How can I do this?
Here's the current method code:
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
}
}
}
Edit:
Maybe just to add some clarification for what I'm needing help with... I have a custom collection that's used in a class being evaluated by a method using reflection. This collection implements IEnumerable twice... IEnumerable<TValue>
and IEnumerable<KeyValuePair<TKey, TValue>>
. Instead of this...
IEnumerable<object> secondaryCollection = ((IEnumerable)secondaryCollectionObject).Cast<object>();
which ends up using the IEnumerable<TValue>
because of the Cast<T>()
operation, I need something where the secondaryCollection
uses the IEnumerable<KeyValuePair<TKey, TValue>>
. And it can't know that the collection originally used two implementations. Since this line:
Type collectionType = primaryMember.FieldType.GetBaseTypes().FirstOrDefault(t => t.IsGenericType && t.GetGenericArguments().Length == 1 && t == genericType.MakeGenericType(t.GetGenericArguments()));
does identify the correct type, I originally thought it could be used but I'm not sure how.