我正在编写一些涉及一些讨厌的反射黑客的代码。目前,我正在创建一个类的实例以及该类的List<T>
容器,其中使用的类由字符串确定。到目前为止,所有内容都包含在同一个程序集中。
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
是一个字符串,包含用于列表的对象和泛型部分的类的名称。现在我想创建一个该类型的实例并将其添加到容器中:
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
"发表评论。我做了以下操作:
首先我创建了一个虚拟类:
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
要复杂得多,这将导致一长串动态调用,如果我们只使用反射来写,就不再清楚发生了什么。