C#反射和实例化-有没有一种方法可以执行Activator.CreateInstance(myType){X=X}?
我不确定这类代码的术语,但我想知道是否有可能在括号后实例化变量,但同时使用反射 我有一个从XML文件加载的地图。这是(int X,int Y,string S)的集合,其中X,Y是某些地形的位置,S是表示地形类型的字符串。我有一本字典可以在字符串和相关类型之间传递;例如,一个键值对可能是“Tree”,typeof(Tree) 使用反射时,虽然我知道可以使用参数实例化,但我唯一感到舒服的方法就是使用Activator.CreateInstance(类型t),即使用空构造函数 当我硬编码映射时,我最初会这样实例化(在一些I,j for循环中): 在开始考虑反射和我的保存文件时,我将此更改为:C#反射和实例化-有没有一种方法可以执行Activator.CreateInstance(myType){X=X}?,c#,reflection,instantiation,C#,Reflection,Instantiation,我不确定这类代码的术语,但我想知道是否有可能在括号后实例化变量,但同时使用反射 我有一个从XML文件加载的地图。这是(int X,int Y,string S)的集合,其中X,Y是某些地形的位置,S是表示地形类型的字符串。我有一本字典可以在字符串和相关类型之间传递;例如,一个键值对可能是“Tree”,typeof(Tree) 使用反射时,虽然我知道可以使用参数实例化,但我唯一感到舒服的方法就是使用Activator.CreateInstance(类型t),即使用空构造函数 当我硬编码映射时,我最
world.Add( new Tree() { X = i, Y = j }
但是,我意识到这不适用于反射,因此我必须执行以下操作(树继承自地形,字典只是将XML保存数据字符串转换为类型):
我更喜欢用类似的方法来做这件事
Type type = dictionary[dataItem.typeAsString];
world.Add((Terrain)Activator.CreateInstance(type) { X = i, Y = j }
有这样的捷径吗?我想如果不是的话,我可以编辑world.Add以获取X和Y,并将其投射到地形中以访问这些变量,但我仍然好奇a)这个{var1=X,var2=Y}编程被称为什么,和b)使用反射时是否存在类似的内容。此语法称为对象初始值设定语法,只是设置属性的语法糖 代码
var result=new MyType{X=X}
将编译为:
MyType __tmp = new MyType();
__tmp.X = x;
MyType result = __tmp;
如果您仅在运行时知道实例化的类型,则必须使用PropertyInfo.SetValue
,或者如果在编译时知道该类型,则必须使用普通的属性设置器
(T)Activator.CreateInstance(typeof(T), param1, param2, ...);
如前所述。您可以创建一个接受这些参数的构造函数,然后使用
但是您将无法使用对象初始化语法。这只是设置属性的糖糖。答案是否定的,因为您提到的(在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" });
var foo=supercallider.Initialize(新的{Bar=“baz”});
代码应该是这样的(这是我脑子里想不出来的)
公共密封级超级对撞机
{
公共静态T初始化(对象属性源)
{
//可以为没有默认构造函数的类型提供重载
var result=Activator.CreateInstance(typeof(T));
foreach(ReflectionHelper.GetProperties中的var prop(typeof(T)))
ReflectionHelper.SetPropertyValue(
结果,//目标
prop,//属性info
propertySource);//我们从哪里获得值
}
}
您必须从匿名对象获取每个属性,在目标类型中找到一个具有相同确切名称和类型的属性,然后从匿名对象中的该属性获取值,并将目标属性的值设置为该值。这并不是难以置信的困难,但它绝对容易出现运行时异常和问题,编译器为匿名类型的属性选择不同的类型,要求您更加具体(例如,new{Bar=(string)null}
),这与事物的优雅性有关。公共密封类ReflectionUtils
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;}
}
{
公共静态T对象初始化器(操作初始化)
{
var result=Activator.CreateInstance();
初始化(结果);
返回结果;
}
}
公共类MyModel
{
公共字符串名称{get;set;}
}
然后再打电话:
var myModel = ReflectionUtils.ObjectInitializer<MyModel>(m =>
{
m.Name = "Asdf"
});
var myModel=ReflectionUtils.ObjectInitializer(m=>
{
m、 Name=“Asdf”
});
这样做的好处是,您将具有类型安全性,并将反射作为最低要求使用,因为我们都知道反射是一种昂贵的操作,应该尽可能避免。使用PropertyInfo.SetValue是否比仅强制转换到我知道类型将从中继承的基类更好?(地形)@Haighstrom:不,不是。强制转换为基类型并使用其中定义的属性是首选的,因为它更易于阅读,重构安全,性能更好。好的,谢谢。在这种情况下,我将继续从World.Add函数中强制转换和设置变量,我可能需要为其设置几个重载。或者,我也可以咬紧牙关,按照下面Zarathos的回答,使用参数反射。@Haighstrom:是的,两种方法都有效。这实际上取决于您希望如何对类进行建模。使用
Zarathos
方法意味着可能通过此方法实例化的每个派生类都需要一个构造函数,该构造函数需要基类型上的属性参数。不多不少。嗯,这是真的,好警察
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
}
}
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"
});