C#反射和实例化-有没有一种方法可以实现Activator.CreateInstance(myType){X=X}
本文关键字:Activator 实现 CreateInstance myType 一种 实例化 反射 有没有 方法 | 更新日期: 2023-09-27 17:54:05
我不确定这类代码的术语,但我想知道是否可以在括号后实例化变量,但同时使用反射。
我有一个从XML文件加载的映射。这是(int X,int Y,string S(的集合,其中X,Y是某些地形的位置,S是表示地形类型的字符串。我有一本字典可以在字符串和相关类型之间传递;例如,一个键值对可能是"Tree",typeof(Tree(。
在使用反射时,尽管我知道可以使用参数实例化,但我感到舒服的唯一方法就是使用Activator.CreateInstance(Type t(,即使用空构造函数。
当我对地图进行硬编码时,我最初会这样实例化(在一些I,j表示循环中(:
case: "Tree"
world.Add( new Tree(i,j) );
在开始考虑反射和我的保存文件时,我将其更改为:
world.Add( new Tree() { X = i, Y = j }
然而,我意识到这对反射不起作用,所以我必须做以下操作(Tree继承自Terrain,字典只是将XML保存数据字符串转换为一种类型(:
Type type = dictionary[dataItem.typeAsString];
Terrain t = (Terrain)Activator.CreateInstance(type);
t.X = i;
t.Y = j;
world.Add(t);
我更喜欢使用这样的东西
Type type = dictionary[dataItem.typeAsString];
world.Add((Terrain)Activator.CreateInstance(type) { X = i, Y = j }
有这样的捷径吗?我想若并没有,我可以编辑这个世界。添加X和Y并投射到Terrain中以访问这些变量,但我仍然很好奇a(这个{var1=X,var2=Y}编程被称为什么,以及b(在使用反射时是否存在类似的东西。
此语法称为Object Initializer语法,只是设置属性的语法糖。
代码var result = new MyType { X = x }
将被编译为:
MyType __tmp = new MyType();
__tmp.X = x;
MyType result = __tmp;
如果您只在运行时知道实例化的类型,则必须使用PropertyInfo.SetValue
自己完成此操作;如果在编译时已知该类型,则需要使用普通的属性setter。
答案是否定的,因为您提到的对象初始化语法(在3.0中随LINQ引入(是编译器的幻觉。如中所示,当您键入此时
var foo = new Foo { Bar = "baz" };
编译器实际上将其转换为符合CLS的IL,这相当于
var foo = new Foo();
foo.Bar = "baz";
Phil Haack有一篇很棒的博客文章,不仅介绍了编译器进行重写的细节,还介绍了在处理实现IDisposable
的类型时可能产生的一些副作用
由于所有这些都只是编译器的佯攻,因此不存在使用反射(即Activator.CreateInstance(Type t)
(的等效方法。其他人会给你变通办法,但最终真的没有直接的对等方法。
你能管理的最接近的通用破解可能是创建一个接受对象的方法,然后使用反射来识别该对象的属性及其各自的值,以便为你执行对象初始化。它可能会像这个一样使用
var foo = Supercollider.Initialize<Foo>(new { Bar = "baz" });
代码会像一样(这是我脑子里想不出来的(
public sealed class Supercollider
{
public static T Initialize<T>(object propertySource)
{
// you can provide overloads for types that don't have a default ctor
var result = Activator.CreateInstance(typeof(T));
foreach(var prop in ReflectionHelper.GetProperties(typeof(T)))
ReflectionHelper.SetPropertyValue(
result, // the target
prop, // the PropertyInfo
propertySource); // where we get the value
}
}
您必须从匿名对象中获取每个属性,在目标类型中找到一个名称和类型完全相同的属性,然后从匿名对象的该属性中获取值,并将目标属性的值设置为该值。这并不难,但它绝对容易出现运行时异常和问题,编译器会为匿名类型的属性选择不同的类型,要求您更具体(例如new { Bar = (string)null }
(,这会影响事情的优雅性。
(T)Activator.CreateInstance(typeof(T), param1, param2, ...);
如本文所述。
public sealed class ReflectionUtils
{
public static T ObjectInitializer<T>(Action<T> initialize)
{
var result = Activator.CreateInstance<T>();
initialize(result);
return result;
}
}
public class MyModel
{
public string Name{get;set;}
}
然后打电话:
var myModel = ReflectionUtils.ObjectInitializer<MyModel>(m =>
{
m.Name = "Asdf"
});
这样做的好处是,您将具有类型安全性,并将反射作为最低要求,因为我们都知道反射是一种昂贵的操作,应该尽可能避免。
您可以创建一个接受这些参数的构造函数,然后使用
Activator.CreateInstance(type, i, j)
但是您将无法使用对象初始化语法。这只是设置属性的糖糖。