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(在使用反射时是否存在类似的东西。

C#反射和实例化-有没有一种方法可以实现Activator.CreateInstance(myType){X=X}

此语法称为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)

但是您将无法使用对象初始化语法。这只是设置属性的糖糖。