1

I noticed a strange behaviour with generic classes using a list of interfaces as a constructor parameter.

Let's say we have the following class

public class GenericClass<T> where T : IInterface
{
    public GenericClass(int inInt, List<T> inList){
    }

    public GenericClass(int inInt, object inObject){
    }
}

When I try to create an instance like this (tmpType implements IInterface):

IEnumerable<IInterface> tmpSomeObjects = xy;

Activator.CreateInstance(typeof(GenericClass<>).MakeGenericType(tmpType), 5, (List<IInterface>)tmpSomeObjects);

The second constructor will be called (int, object).

I probably miss an important point... I expected the first constructor to be executed.

3
  • What is ChangeTable<> ? Commented Apr 26, 2018 at 11:24
  • @LasseVågsætherKarlsen updated my post, copy/paste error. Commented Apr 26, 2018 at 11:25
  • 1
    And why did you expect the first constructor be called? It explicitly asks for a List<T>, and we don't know the actual type of xy or tmpSomeObjects (other than what you've declared the variable as). Commented Apr 26, 2018 at 11:25

2 Answers 2

4

Your IEnumerable is of type IEnumerable<IInterface>, but the type you are constructing has a generic parameter of a derived type, so it does not match the exact constructor.

Say T is Foo (which implements IInterface), your type becomes:

public class GenericClass<Foo>
{
    public GenericClass(int inInt, List<Foo> inList){
    }

    public GenericClass(int inInt, object inObject){
    }
}

Yet you are passing an IEnumerable<IInterface> (or List<IInterface>) to it, which doesn't match List<Foo>, so that's why it's preferring object (not only it's preferred... the other constructor won't match at all).

Try it: remove the constructor with object and try to do this:

var list = new List<IInterface>();
var x = new GenericClass<TypeImplementingIInterface>(5, list);

That won't even compile.

So the solution in your case would be simple... make the parameter in the constructor IEnumerable<IInterface>, instead of List<T>, which is what you actually want to pass it

Sign up to request clarification or add additional context in comments.

1 Comment

I mixed up runtime and compile time. Thanks for the clarification! Indeed your answer would solve the problem, but on the other hand in case of a change to 'T : IInterface', I'd have to change the constructor parameter as well. :-)
3

You are trying to do this:

var list = new List<IInterface>();
new GenericClass<TmpType>(5, list);

However, List<IInterface> is not convertible to List<TmpType>, even though TmpType implements IInterface, so overload with object is chosen.

If you try with:

var list = new List<TmpType>();
// should work with Activator.CreateInstance too
new GenericClass<TmpType>(5, list);

Then it should choose first one.

Note that with Activator.CreateInstance, unlike "manual" invocation, runtime type of list does matter. So for example, this:

IEnumerable<IInterface> list = new List<TmpType>();   
Activator.CreateInstance(typeof(GenericType<>)).MakeGenericType(typeof(TmpType)), 5, list);

will choose first overload, because runtime-type is List<TmpType>. However this:

IEnumerable<IInterface> list = new List<TmpType>();
new GenericClass<TmpType>(1, list);

will chose second (with object), because now constructor is resolved at compile time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.