Cannot add classes created via reflection to a container

Viewed 81

I'm working on some code that involves some nasty reflection hacking. Currently, I am creating a both a instance of a class as well as a List<T> container for that class where the class used is determined by a string. Everything as of right now is contained within the same assembly.

Type containerType = typeof(List<>);

Assembly currentAsm = Assembly.GetExecutingAssembly();
Type applicationModelType = currentAsm.GetType("NamespaceName." + targetApplicationModel);

Type applicationModelContainerType = containerType.MakeGenericType(applicationModelType);
dynamic container = Activator.CreateInstance(applicationModelContainerType);

In this case targetApplicationModel is a string containing the name of a class used for both the objects and generic part for the list. Now I want to create an instance of that type and add it to the container:

var x = Activator.CreateInstance(applicationModelType);
container.Add(y);
var y = container.Item[0];

However the call to Add fails with

Exception thrown: 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' in System.Core.dll
An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll
The best overloaded method match for 'System.Collections.Generic.List<MyType>.Add(MyType)' has some invalid arguments

I can be pretty certain by inspecting while debuggung that x is indeed an instance of MyType so I have no idea why the call fails.

2 Answers

To follow up on my "don't use dynamic" comment. I did the following:

First I created a dummy class:

public class ClassToCollect
{
    public string SomeProperty { get; set; } = "Hello World";
}

Then I ran this code:

var containerType = typeof(List<>);
var itemType = typeof(ClassToCollect);
var fullContainerType = containerType.MakeGenericType(itemType);
var container = Activator.CreateInstance(fullContainerType);
var addMethod = fullContainerType.GetMethod("Add");
var objectToAdd = Activator.CreateInstance(itemType);
addMethod.Invoke(container, new object[] { objectToAdd });

At the end of that code, I can see one item in the list.

As long as we're on a "don't use dynamic" kick, I like to throw in a "don't use Activator.CreateInstance" approach, since it has traditionally performed very poorly compared to invoking constructors (recent versions of .NET have remedied this a lot, but still). To avoid some of the nastier bits of reflection it tends to pay off to write code as you would "usually", and then invoke it dynamically only at the last step.

static List<T> createSingleElementList<T>() where T : new() => new() { new() };

Type applicationModelType = ...;
object container = ((Func<List<object>>) createSingleElementList<object>)
    .Method
    .GetGenericMethodDefinition()
    .MakeGenericMethod(applicationModelType)
    .Invoke(null, null);

This code uses various features available only in newer versions of C# (static local methods, implicitly typed new) but these are not important; you can write createSingleElementList in other ways if you please. The core technique is extracting a generic method from the function and invoking that at runtime with the desired type. In this particular case we don't gain that much from writing things this way, but you can imagine createSingleElementList being much more complicated, which would result in a long list of dynamic calls where it's no longer clear what's happening if we wrote it using reflection alone.

Type safety aside, even this is not the absolute fastest way to do things, but optimizing dynamic code is another question altogether.

Related