【问题标题】:How to create instance of a class from class name without using reflection?如何在不使用反射的情况下从类名创建类的实例?
【发布时间】:2013-06-13 12:19:30
【问题描述】:

有没有办法实现这样的目标?

如果将"Employee" 作为参数传递给方法,则它应该返回Employee 类型的对象。

但不使用反射。

【问题讨论】:

  • 反思会有所帮助。问题是:为什么?
  • 如果没有反思,你是不会去做的。简而言之,这就是反射:将名称/元数据与代码相关联。你为什么要不反思就去做呢?这就像问如何在不打开门的情况下打开一扇门。
  • @Krizz 这个问题的不同之处在于他不想使用反射。一个值得回答的问题——或者更确切地说,解释为什么无法回答。
  • 如果你得到你想要的信息,不要忘记将答案标记为已接受

标签: c# c#-4.0 initialization instantiation


【解决方案1】:

您可以使用Type.GetType(string) 来获取该类型的元数据。但是,这需要类型的Assembly Qualified Name,除非该类型驻留在当前执行的程序集中是 mscorlib.dll 的一部分。

然后就可以使用Activator.CreateInstance(Type)获取实例了。

var type = Type.GetType(typeName);
var obj = Activator.CreateInstance(type);

此时obj的静态类型为System.Object。您需要继续使用反射来获取在实际类型上定义的属性和方法,或者您可以将对象视为dynamic,假设您在编译时不知道将结果转换为哪个类(如果你确实知道,你会跳过整个过程)。


编辑:加上你不想使用反射的限制,这会改变你的选择。就您可以支持的内容而言,代码不会像 动态 那样,您通常需要提前有一个想法,但这可能是一件好事,具体取决于您要尝试的内容完成。您可能拥有的只是一个 switch 语句或支持类型的字典,将名称作为字符串键入。

public object GetInstanceOf(string typeName)
{
    switch (typeName)
    {
        case "Employee": return new Employee();
        case "Manager" : return new Manager();
        case "Owner" : return new Owner();
        // etc
        default: 
            throw new InvalidOperationException("typeName is not supported");
    }
}

请注意,使用这种方法,您可以提前了解所有支持的类型。在代码之外还有其他方法可以提前了解类型(例如:配置、数据),但这些方法通常会让你回到答案的第一部分。另请注意,您的返回类型仍然有限。它必须是所涉及类的通用基类型或接口。在我的代码示例中,它是所有类和结构的通用基本类型System.Object。对你来说,这可能更像是一个工厂,带有Worker 基类或IWorker 接口。或者Employee 是基础,而您的方法正在构建它的专门子级。后两个示例使您可以在编译时访问基本或接口定义的方法和属性。

【讨论】:

  • 问题:为什么这里的一些答案直接使用带有字符串的 CreateInstance() 而有些则首先使用 Type 对象?
  • 他们正在使用接受(string assemblyName, string typeName)different overload。在幕后,它很可能在做同样的事情,使用参数构造一个程序集限定引用来获取类型元数据。或者也许它正在做一些不同的事情,最终结果是一样的。
  • 酷;我主要想知道这些方法是否存在直接的实质性差异。这不是我自己必须做的事情,所以我不确定。
【解决方案2】:

是的,你可以在“Reflection”的帮助下做到这一点

试试

Employee employee =(Employee)Activator.CreateInstance("Employee"); 

检查 @jon skeet 答案:How do I create an instance from a string in C#?

【讨论】:

  • 你能不能给个链接什么的,我搜索 Refelection 但我得到了垃圾!
  • @Sean87 尝试搜索 reflection C#reflection .NET
【解决方案3】:

在没有反射的情况下实例化任意类型

我错了。似乎有很多方法可以在没有真正反射的情况下实例化类型。我会尽量整理一份我能找到的所有东西的清单。

泛型

根据您想要做的事情,您也许可以使用一种非常酷的技术,称为泛型。您不能在运行时输入任意类型的名称,因此这不一定能完全回答您的问题,但如果您在编译时知道所需的类型,这将是一个很棒的工具。 这不涉及任何形式的反射,而是完全在编译时。这是一个例子:

interface IParsable
{
    bool TryParse(string text);
}

class MyInt : IParsable
{
    public int Value { get; private set; }

    public static MyInt Parse(string text)
    {
        Parser parser = new Parser();
        return parser.Parse<MyInt>(text);
    }
}

class MyFloat : IParsable
{
    public float Value { get; private set; }

    public static MyFloat Parse(string text)
    {
        Parser parser = new Parser();
        return parser.Parse<MyFloat>(text);
    }
}

class Parser
{
    // The "new()" constraint means that T must have a
    // parameterless constructor.
    private T Parse<T>(string text)
        where T : IParsable, new()
    {
        // Even though T isn't actually a type, we can use
        // it as if it were, for the most part.
        T obj = new T();

        // Because we had the IParsable constraint, we can
        // use the TryParse method.
        if (!obj.TryParse(text))
        {
            throw new Exception("Text could not be parsed.");
        }

        return obj;
    }
}

Lambda 字典

感谢 Anthony Pegram 在这方面的天才(见下面的 cmets)。以前我使用反射来解决这个问题,但由于 lambda 表达式,他将其修复为无需任何反射即可工作。

static readonly IDictionary<string, Func<object>> Types = new Dictionary<string, Func<object>>()
{
    { "TypeA", () => new TypeA() },
    { "TypeB", () => new TypeB() },
    { "TypeC", () => new TypeC() },
};

// If you're okay with a bit of reflection behind-the-scenes, change "object"
// here to "dynamic", and you won't have to cast down the road.
object void GetInstance(string name)
{
    if (Types.ContainsKey(name))
    {
        return Types[name]();
    }
    else
    {
        return null;
    }
}

预实例化对象

另一种选择是每次都返回相同的引用。这完全避免了“真正的”反射。这种重用实例的想法有一些重要的含义,这可能是好的也可能是坏的,这取决于你在做什么。这些含义非常有趣,如果使用得当,可能会非常惊人。

如果你愿意,你可以让每个类型实现一个特定的接口,然后强制转换为那个接口,而不是返回一个原始对象。

static readonly IDictionary<string, object> Instances = new Dictionary<string, object>()
{
    { "TypeA", new TypeA() },
    { "TypeB", new TypeB() },
    { "TypeC", new TypeC() },
};

object void GetInstance(string name)
{
    if (!Instances.ContainsKey(name))
    {
        return null;
    }

    return Instances[name];
}

使用反射实例化任意类型

如果你的类型有一个无参数的构造函数,你会得到一个很好的答案数组。但如果没有呢?

const string TYPE = "System.String";
Type type = Type.GetType(TYPE);
if (type == null)
{
    // Type doesn't exist--at least, not in mscorlib or current assembly,
    // or we didn't specify the assembly.
    throw new Exception("Could not find type " + TYPE + ".");
}

// Note the Type array.  These are the types of the parameters that the
// constructor takes.
ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(char), typeof(int) });
if (ctor == null)
{
    // Constructor doesn't exist that takes those parameters.
    throw new Exception("Could not find proper constructor in " + TYPE + ".");
}

// Note the object array.  These are the actual parameters passed to the
// constructor.  They should obviously match the types specified above.
string result = (string)ctor.Invoke(new object[] { 'a', 5 });

【讨论】:

  • 请注意,您还可以使用Dictionary&lt;string, Func&lt;object&gt;&gt; 使用字典方法,您可以使用dictionary.Add("Foo", () =&gt; new Foo());。这允许您拥有return dictionary[typeName]();,从而另一种方式来消除使用类型元数据,同时始终生成一个新实例。
  • 哇,不错,@AnthonyPegram。我从来没想过。我希望你不介意,但我编辑了它,因为这是个好主意。
  • 没关系。在有用的时候加入 cmets 是非常公平的游戏。
  • 我根本不理解您的第二种方法(泛型之一)。如果我有T 类型参数,那么有什么麻烦呢? OP只有字符串(即使那样我认为你的设计复杂了。你的IParsable需要的只是string Name { get; set; })。希望您知道new T() 在技术上确实在幕后使用反射,而且速度较慢。这是一个相关链接stackoverflow.com/questions/6069661/…。除了泛型之一,很好的答案!
  • @nawfal 有趣。我倾向于指定将其限制为值或引用类型的约束,所以我想这就是我从未见过的原因。无论如何,很容易避免使用约束。
【解决方案4】:

你可以使用Activator.CreateInstance()

Employee employee =(Employee)Activator.CreateInstance("Namespace", "Employee");

【讨论】:

    【解决方案5】:

    @vulkanino 所说的使用反射会以这样的方式结束:

    Employee instance = (Employee)Activator.CreateInstance("MyNamespace.Employee, MyAssembly");
    

    希望对你有所帮助。

    【讨论】:

      【解决方案6】:

      使用reflection,您可以在程序集中找到类型,无论是正在执行的程序集还是其他已加载的程序集(实际上,您可以按需加载它们)。如果没有指定如何在您的场景中工作的完整示例,那么您将使用类似于 Activator.CreateInstance 的内容来创建找到的对象的实例。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-21
        • 2014-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多