如何在不使用反射的情况下从类名创建类的实例?

时间:2012-02-09 14:40:47

标签: c# c#-4.0 initialization instantiation

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

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

但不使用反射。

6 个答案:

答案 0 :(得分:2)

您可以使用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。对于您来说,这可能更像是 factory ,具有Worker基类或IWorker接口。或者也许Employee是基础,你的方法正在构建它的专门孩子。后两个示例为您提供了对基本或接口定义的方法和属性的编译时访问。

答案 1 :(得分:1)

是的,你可以在“反思

的帮助下做到

尝试

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

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

答案 2 :(得分:1)

实例化没有反射的任意类型

我错了。有很多方法可以在没有真实反射的情况下实例化一个类型。我会尝试编制一个我能找到的所有列表。

泛型

根据您的尝试,您可能会做一种非常酷的技术,称为泛型。您不能在运行时输入任意类型的名称,因此这不一定完整地回答您的问题,但如果您在编译时知道所需的类型,这将成为一个很好的工具。 这不涉及任何类型的反射,但完全是编译时间。这是一个例子:

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;
    }
}

Lambdas字典

感谢Anthony Pegram在这一天的天赋(见下面的评论)。以前我使用反射这个,但是由于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 });

答案 3 :(得分:0)

您可以使用Activator.CreateInstance()

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

答案 4 :(得分:0)

使用反射作为@vulkanino说你会以这样的结尾:

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

希望这会对你有所帮助。

答案 5 :(得分:0)

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