无法将通过反射创建的 类 添加到容器中

Cannot add classes created via reflection to a container

我正在编写一些涉及一些讨厌的反射黑客攻击的代码。目前,我正在创建 class 的实例以及 class 的 List<T> 容器,其中使用的 class 由字符串确定。目前所有内容都包含在同一个程序集中。

Type containerType = typeof(List<>);

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

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

在这种情况下,targetApplicationModel 是一个包含 class 名称的字符串,用于列表的对象和通用部分。现在我想创建一个该类型的实例并将其添加到容器中:

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

但是对 Add 的调用失败

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

通过在调试时进行检查,我可以非常确定 x 确实是 MyType 的一个实例,所以我不知道调用失败的原因。

跟进我的“不要使用 dynamic”评论。我做了以下事情:

首先我创建了一个虚拟对象 class:

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

然后我运行这个代码:

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

在该代码的末尾,我可以看到列表中的一项。

只要我们处于“不使用 dynamic”的状态,我就喜欢采用“不使用 Activator.CreateInstance”的方法,因为传统上它执行与调用构造函数相比非常差(最新版本的 .NET 对此进行了很多补救,但仍然如此)。为了避免一些更糟糕的反射,它往往会像您“通常”那样编写代码,然后仅在最后一步动态调用它。

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);

此代码使用了仅在较新版本的 C# 中可用的各种功能(静态本地方法,隐式类型 new),但这些并不重要;如果你愿意,你可以用其他方式写 createSingleElementList 。核心技术是从函数中提取一个通用方法,并在运行时使用所需的类型调用它。在这种特殊情况下,我们不会从这种方式中获得太多收益,但您可以想象 createSingleElementList 会复杂得多,这将导致一长串动态调用,如果我们不再清楚发生了什么仅使用反射编写它。

撇开类型安全不谈,即使这不是绝对最快的做事方式,但优化动态代码完全是另一个问题。