Search code examples
c#genericsreflectionarity

Why would this code complain about "the arity of the generic type definition"?


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 throws 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).


Solution

  • 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;
            }
        }
    }