无法将通过反射创建的 类 添加到容器中
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
会复杂得多,这将导致一长串动态调用,如果我们不再清楚发生了什么仅使用反射编写它。
撇开类型安全不谈,即使这不是绝对最快的做事方式,但优化动态代码完全是另一个问题。
我正在编写一些涉及一些讨厌的反射黑客攻击的代码。目前,我正在创建 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
会复杂得多,这将导致一长串动态调用,如果我们不再清楚发生了什么仅使用反射编写它。
撇开类型安全不谈,即使这不是绝对最快的做事方式,但优化动态代码完全是另一个问题。