views:

59

answers:

1

I've got a generic type:

class DictionaryComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>

And a factory method that will (should) create an instance of this class for a given dictionary type.

    private static IEqualityComparer<T> CreateDictionaryComparer<T>()
    {
        Type def = typeof(DictionaryComparer<,>);
        Debug.Assert(typeof(T).IsGenericType);
        Debug.Assert(typeof(T).GetGenericArguments().Length == 2);

        Type t = def.MakeGenericType(typeof(T).GetGenericArguments());

        return (IEqualityComparer<T>)Activator.CreateInstance(t);
    }

Stripping away all of the extraneous stuff - even this code thorws the same exception.

private static object CreateDictionaryComparer()
{
    Type def = typeof(DictionaryComparer<,>);

    Type t = def.MakeGenericType(new Type[] { typeof(String), typeof(object) });

    return Activator.CreateInstance(t);
}

The Asserts pass so I know that T is generic and has two generic arguments. The line with MakeGenericType however excepts with: "The number of generic arguments provided doesn't equal the arity of the generic type definition. Parameter name: instantiation"

I've done this sort of thing in the past and for the life of me can't figure out why this isn't working in this case. (plus I had to Google arity).

+2  A: 

Figured it out.

I had DictionaryComparer declared as an inner class. I can only assume that MakeGenericType wanted to make a Query<T>.DictionaryComparer<string,object> and was not provided T.

Failing code

class Program
{
    static void Main(string[] args)
    {
        var q = new Query<int>();
        q.CreateError();
    }
}

public class Query<TSource>
{
    public Query()
    {    
    }

    public object CreateError()
    {
        Type def = typeof(DictionaryComparer<,>);

        Type t = def.MakeGenericType(new Type[] { typeof(String), typeof(object) });

        return Activator.CreateInstance(t);
    }

    class DictionaryComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>
    {
        public DictionaryComparer()
        {
        }

        public bool Equals(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
        {
            if (x.Count != y.Count)
                return false;

            return GetHashCode(x) == GetHashCode(y);
        }

        public int GetHashCode(IDictionary<TKey, TValue> obj)
        {
            int hash = 0;
            unchecked
            {
                foreach (KeyValuePair<TKey, TValue> pair in obj)
                {
                    int key = pair.Key.GetHashCode();
                    int value = pair.Value != null ? pair.Value.GetHashCode() : 0;
                    hash ^= key ^ value;
                }
            }
            return hash;
        }
    }
}
dkackman
Just out of curiosity, could you provide the full (failing) sample so I can try it on my compiler?
Richard Cook
Lemme see if I can strip unrelated stuff out to a compilable example
dkackman
Thanks! Moving `DictionaryComparer<TKey, TValue>` outside the generic container class `Query<TSource>` fixes the issue. I experimented and you can nest `DictionaryComparer<TKey, TValue>`, just not inside another generic class. Also wanted to make sure my compiler and runtime environment were behaving the same as yours.
Richard Cook
Mine is vs.net 2010 and .net 4.
dkackman
As a further experiment providing three types new Type[] { typeof(int), typeof(String), typeof(object) } also works, satisfying the need to make a concrete type for the containing class (though removing the inner class is the better solution)
dkackman
Good to know. I believe `gmcs` 1.9.x is roughly equivalent to the Microsoft C# 3.0 compiler.
Richard Cook