detailed explanation of the problem. I will definitely add this in.
have stopped all this in the first place. I am going to switch from
typeof(ICollection<>).Name to typeof(ICollection<>).FullName. With
"ICollection`1". Unfortunately I can't specify the assembly in there
as well. So far this seems to be the most reliable way of testing for
a unclosed generic interface, unfortunately.
> Hi Ted,
> Thanks for a brilliant class. Since you have taken the time to put
> this together and test it I thought it only fair to share a suggested
> modification with you and others. First let me add some context.
> The C5 Generic Collection Library is produced and published by the IT
> University of Copenhagen who describe it thus:
> C5 is a library of generic collection classes for C# and other CLI
> languages and works with Microsoft .Net version 2.0 and later, and
> Mono version 1.2 and later.
> C5 provides functionality and data structures not provided by the
> standard .Net System.Collections.Generic namespace, such as persistent
> tree data structures, heap based priority queues, hash indexed array
> lists and linked lists, and events on collection changes. Also, it is
> more comprehensive than collection class libraries on other similar
> platforms, such as Java. Unlike many other collection class libraries,
> C5 is designed with a strict policy of supporting "code to interface
> not implementation".
> I have been using this library because of its superior performance and
> its ability to generate events when various actions are performed on a
> collection.
> I experienced a problem using this library with JsonExSerializer which
> triggered an unhandled "Ambiguous Match Exception" during the
> serialization process. After giving some consideration to seeing how
> easy it would be to implement a custom collection handler for the
> serialisation of this object and searching the site for information on
> how to do this the thought struck me that the serializer was reporting
> (indirectly via the exception) not that it didn't understand about the
> C5 classes but that at some critical point it either couldn't or
> didn't distinguish between the standard generic collection types and
> their C5 counterparts.
> After tracing through the code the problem area was quickly
> established. Not surprisingly it lies in the current implementation of
> the GenericCollectionHandler class so no banana there! I want on to
> modify the code slightly in order to better understand why it was
> failing and to see if anything could be done without affecting the
> integrity of it.
> I found that with two relatively straight forward modifications the
> problem could be easily resolved. If you accept these mods you may, of
> course, want to implement them slightly differently or tighten up on
> the error checking.
> The first mod concerns the IsCollection() method, which is where the
> exception was being thrown.
> In the original code below the call to collectionType.GetInterface
> failed because the C5 collection contained
> two references that matched the _IGenericCollectionName, one with a C5
> namespace and one with the standard generic collections namespace.
> public override bool IsCollection(Type collectionType)
> {
> return (!collectionType.IsArray
> &&
> collectionType.GetInterface(_IGenericCollectionName) != null
> && !
> _IDictionaryType.IsAssignableFrom(collectionType));
> }
> It occurred to me that at this point the check is only to see if the
> interface exists not whether multiples can be resolved. To this end I
> modified the code to
> public override bool IsCollection(Type collectionType)
> {
> bool isCollection = false;
> if (!collectionType.IsArray)
> {
> if (SupportsInterface(collectionType,
> _IGenericCollectionName))
> {
> if (!
> _IDictionaryType.IsAssignableFrom(collectionType))
> {
> isCollection = true;
> }
> }
> }
> return isCollection;
> }
> The nested 'if' statements came about out of the need to identify
> which part of the test was failing and why so these could now be
> collapsed again if so desired. The middle condition has been changed
> to call a new function called 'SupportsInterface' given below. This
> version will return true if one *or more* matches against the given
> interface can be made.
> private bool SupportsInterface(Type collectionType, string
> interfaceType)
> {
> bool interfaceSupported = false;
> try
> {
> interfaceSupported =
> (collectionType.GetInterface(interfaceType) != null);
> }
> catch (AmbiguousMatchException ambiguousMatch)
> {
> // Ambuguity means that the interface is supported.
> Whether the ambiguity can be resolved is
> // not relevant at this point
> interfaceSupported = true;
> }
> return interfaceSupported;
> }
> This allowed the serialization process proceed further to the point
> where a specific instance of the interface needs to be obtained in the
> GetItemType() method. The solution adopted here is to obtain an array
> containing the matching interfaces and then find the one whose
> namespace matches that of the given object. Whilst this approach may
> not be foolproof in all cases (I will pass that on to you for
> consideration), it certainly solved the problem for my particular
> case.
> The original code
> public override Type GetItemType(Type CollectionType)
> {
> Type intfType =
> CollectionType.GetInterface(_IGenericCollectionName);
> return intfType.GetGenericArguments()[0];
> }
> has been modified to replace the call to GetInterface() with a call to
> FindInterfaces() which returns an array of matching items. The array
> is then scanned for an entry whose interface namespace matches that of
> the given object.
> public override Type GetItemType(Type CollectionType)
> {
> Type interfaceType = null;
> // Set up a filter to obtain the all of interfaces that
> match _IGenericCollectionName
> TypeFilter typeFilter = new
> TypeFilter(CollectionInterfaceFilter);
> Type[] types = CollectionType.FindInterfaces(typeFilter,
> _IGenericCollectionName);
> // Find the entry with a namespace that matches the given
> collection
> foreach (Type type in types)
> {
> if (type.Namespace == CollectionType.Namespace)
> {
> interfaceType = type.GetGenericArguments()[0];
> }
> }
> return interfaceType;
> }
> The creation of a TypeFilter requires a helper to perform the actual
> check.
> public static bool CollectionInterfaceFilter(Type type, object
> filterCriteria)
> {
> return type.Name == filterCriteria.ToString();
> }
> With these changes in place I can now serialize and deserialize a
> C5.LinkedList object that resides alongside a standard Dictionary and
> ArrayList (for test purposes). I haven't been able to run your unit
> tests against these mods at this stage as my test setup is somewhat
> different.
> I hope this is of help to you and other users of the serializer.
> Regards
> Bob Bellchambers-Wilson