Como criar uma nova instância de objeto de um Type

748

Nem sempre é possível conhecer o Typeobjeto em tempo de compilação, mas pode ser necessário criar uma instância do Type.

Como você obtém uma nova instância de objeto de a Type?

tags2k
fonte

Respostas:

896

A Activatorclasse dentro do Systemnamespace raiz é bastante poderosa.

Existem muitas sobrecargas para passar parâmetros para o construtor e tal. Confira a documentação em:

http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx

ou (novo caminho)

https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance

Aqui estão alguns exemplos simples:

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");
Karl Seguin
fonte
21
Fico feliz em finalmente ter encontrado isso, mas a segunda chamada não está exatamente correta, faltando uma cotação e pars invertidos, deve ser: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespace.ObjectType");
kevinc
10
Você precisa chamar 'Unwrap ()' para obter o tipo real de objeto que deseja: Instância ConcreteType = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap ();
Ε Г И І И О
4
Como ObjectType instancecorresponde à condição do OP "Nem sempre é possível saber o tipo de um objeto no tempo de compilação"? : P
Martin Schneider
@ MA-Maddin tudo bem então object instance = Activator.CreateInstance(...);.
BrainSlugs83
1
Alguém sabe como fazer isso no .NET Core? O método Unwrap não está disponível no objeto.
Justin
145
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

A Activatorclasse possui uma variante genérica que facilita um pouco isso:

ObjectType instance = Activator.CreateInstance<ObjectType>();
Konrad Rudolph
fonte
8
@ Kevin Claro. Essa operação não pode funcionar em uma linguagem de tipo estaticamente, porque não faz sentido. Você não pode invocar métodos em um objeto de tipo desconhecido. Nesse meio tempo (= desde escrevendo esta resposta) C # tem o dynamicconceito que faz permitir que tais construções, mas na maioria dos casos esta resposta ainda cobre.
Konrad Rudolph
1
@KonradRudolph Não é bem verdade. Primeiro de c # que permitem criar novos tipos em tempo de execução. Você simplesmente não pode ligar para eles de uma maneira estaticamente segura . Então sim, você está meio correto. Mas, de maneira mais realista, você precisa disso quando carrega assemblies em tempo de execução, o que significa que o tipo não é conhecido no momento da compilação. C # seria severamente limitado se você não pudesse fazer isso. Quero dizer, você acabou de provar isso: de que outra forma o método Activator que usa uma instância de tipo funciona? Quando a MS escreveu a classe Activator, eles não tinham conhecimento em tempo de compilação de quaisquer tipos futuros que os usuários escrevessem.
AnorZaken
1
@AnorZaken Meu comentário não diz nada sobre a criação de tipos em tempo de execução. É claro que você pode fazer isso, mas não pode usá-los estaticamente no mesmo contexto (é possível hospedar um programa completo estaticamente compilado). É tudo o que meu comentário está dizendo.
Konrad Rudolph
@KonradRudolph Desculpe, interpretou mal "tal operação" para significar instanciar um tipo conhecido apenas em tempo de execução; em vez de significado, usando um tipo de tempo de execução como um parâmetro de tipo genérico.
AnorZaken
1
@AnorZaken - tecnicamente, você pode criar novos tipos em tempo de execução e chamar métodos neles de uma maneira estaticamente segura se o seu novo tipo implementar uma interface conhecida ou herdar uma classe base conhecida. - Qualquer uma dessas abordagens fornecerá um contrato estático para o objeto criado em tempo de execução.
BrainSlugs83
132

Expressão compilada é a melhor maneira! (para desempenho para criar repetidamente instância em tempo de execução).

static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
 ).Compile();

X x = YCreator();

Estatísticas (2012):

    Iterations: 5000000
    00:00:00.8481762, Activator.CreateInstance(string, string)
    00:00:00.8416930, Activator.CreateInstance(type)
    00:00:06.6236752, ConstructorInfo.Invoke
    00:00:00.1776255, Compiled expression
    00:00:00.0462197, new

Estatísticas (2015, .net 4.5, x64):

    Iterations: 5000000
    00:00:00.2659981, Activator.CreateInstance(string, string)
    00:00:00.2603770, Activator.CreateInstance(type)
    00:00:00.7478936, ConstructorInfo.Invoke
    00:00:00.0700757, Compiled expression
    00:00:00.0286710, new

Estatísticas (2015, .net 4.5, x86):

    Iterations: 5000000
    00:00:00.3541501, Activator.CreateInstance(string, string)
    00:00:00.3686861, Activator.CreateInstance(type)
    00:00:00.9492354, ConstructorInfo.Invoke
    00:00:00.0719072, Compiled expression
    00:00:00.0229387, new

Estatísticas (2017, LINQPad 5.22.02 / x64 / .NET 4.6):

    Iterations: 5000000
    No args
    00:00:00.3897563, Activator.CreateInstance(string assemblyName, string typeName)
    00:00:00.3500748, Activator.CreateInstance(Type type)
    00:00:01.0100714, ConstructorInfo.Invoke
    00:00:00.1375767, Compiled expression
    00:00:00.1337920, Compiled expression (type)
    00:00:00.0593664, new
    Single arg
    00:00:03.9300630, Activator.CreateInstance(Type type)
    00:00:01.3881770, ConstructorInfo.Invoke
    00:00:00.1425534, Compiled expression
    00:00:00.0717409, new

Estatísticas (2019, x64 / .NET 4.8):

Iterations: 5000000
No args
00:00:00.3287835, Activator.CreateInstance(string assemblyName, string typeName)
00:00:00.3122015, Activator.CreateInstance(Type type)
00:00:00.8035712, ConstructorInfo.Invoke
00:00:00.0692854, Compiled expression
00:00:00.0662223, Compiled expression (type)
00:00:00.0337862, new
Single arg
00:00:03.8081959, Activator.CreateInstance(Type type)
00:00:01.2507642, ConstructorInfo.Invoke
00:00:00.0671756, Compiled expression
00:00:00.0301489, new

Estatísticas (2019, x64 / .NET Core 3.0):

Iterations: 5000000
No args
00:00:00.3226895, Activator.CreateInstance(string assemblyName, string typeName)
00:00:00.2786803, Activator.CreateInstance(Type type)
00:00:00.6183554, ConstructorInfo.Invoke
00:00:00.0483217, Compiled expression
00:00:00.0485119, Compiled expression (type)
00:00:00.0434534, new
Single arg
00:00:03.4389401, Activator.CreateInstance(Type type)
00:00:01.0803609, ConstructorInfo.Invoke
00:00:00.0554756, Compiled expression
00:00:00.0462232, new

Código completo:

static X CreateY_New()
{
    return new Y();
}

static X CreateY_New_Arg(int z)
{
    return new Y(z);
}

static X CreateY_CreateInstance()
{
    return (X)Activator.CreateInstance(typeof(Y));
}

static X CreateY_CreateInstance_String()
{
    return (X)Activator.CreateInstance("Program", "Y").Unwrap();
}

static X CreateY_CreateInstance_Arg(int z)
{
    return (X)Activator.CreateInstance(typeof(Y), new object[] { z, });
}

private static readonly System.Reflection.ConstructorInfo YConstructor =
    typeof(Y).GetConstructor(Type.EmptyTypes);
private static readonly object[] Empty = new object[] { };
static X CreateY_Invoke()
{
    return (X)YConstructor.Invoke(Empty);
}

private static readonly System.Reflection.ConstructorInfo YConstructor_Arg =
    typeof(Y).GetConstructor(new[] { typeof(int), });
static X CreateY_Invoke_Arg(int z)
{
    return (X)YConstructor_Arg.Invoke(new object[] { z, });
}

private static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
static X CreateY_CompiledExpression()
{
    return YCreator();
}

private static readonly Func<X> YCreator_Type = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y))
).Compile();
static X CreateY_CompiledExpression_Type()
{
    return YCreator_Type();
}

private static readonly ParameterExpression YCreator_Arg_Param = Expression.Parameter(typeof(int), "z");
private static readonly Func<int, X> YCreator_Arg = Expression.Lambda<Func<int, X>>(
   Expression.New(typeof(Y).GetConstructor(new[] { typeof(int), }), new[] { YCreator_Arg_Param, }),
   YCreator_Arg_Param
).Compile();
static X CreateY_CompiledExpression_Arg(int z)
{
    return YCreator_Arg(z);
}

static void Main(string[] args)
{
    const int iterations = 5000000;

    Console.WriteLine("Iterations: {0}", iterations);

    Console.WriteLine("No args");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(string assemblyName, string typeName)", Creator = (Func<X>)CreateY_CreateInstance},
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<X>)CreateY_CreateInstance},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func<X>)CreateY_Invoke},
        new {Name = "Compiled expression", Creator = (Func<X>)CreateY_CompiledExpression},
        new {Name = "Compiled expression (type)", Creator = (Func<X>)CreateY_CompiledExpression_Type},
        new {Name = "new", Creator = (Func<X>)CreateY_New},
    })
    {
        var creator = creatorInfo.Creator;

        var sum = 0;
        for (var i = 0; i < 1000; i++)
            sum += creator().Z;

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator();
            sum += x.Z;
        }
        stopwatch.Stop();
        Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
    }

    Console.WriteLine("Single arg");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<int, X>)CreateY_CreateInstance_Arg},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func<int, X>)CreateY_Invoke_Arg},
        new {Name = "Compiled expression", Creator = (Func<int, X>)CreateY_CompiledExpression_Arg},
        new {Name = "new", Creator = (Func<int, X>)CreateY_New_Arg},
    })
    {
        var creator = creatorInfo.Creator;

        var sum = 0;
        for (var i = 0; i < 1000; i++)
            sum += creator(i).Z;

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator(i);
            sum += x.Z;
        }
        stopwatch.Stop();
        Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
    }
}

public class X
{
  public X() { }
  public X(int z) { this.Z = z; }
  public int Z;
}

public class Y : X
{
    public Y() {}
    public Y(int z) : base(z) {}
}
Serj-Tm
fonte
18
+1 para todas as estatísticas! Eu realmente não preciso desse tipo de performance no momento, mas ainda assim muito interessante. :)
AnorZaken
1
Também há TypeDescriptor.CreateInstance (ver stackoverflow.com/a/17797389/1242 ) que pode ser mais rápida se for utilizado com TypeDescriptor.AddProvider
Lars Truijens
2
Isso ainda é útil quando você não sabe que tipo Xé em tempo de execução?
ajeh
1
@ajeh Yes. Altere typeof (T) para Type.GetType (..).
Serj-Tm
3
@ Serj-Tm Não, isso não funcionará se o tipo X for um tempo de execução Type.
NetMage
47

Uma implementação desse problema é tentar chamar o construtor sem parâmetros do Type:

public static object GetNewObject(Type t)
{
    try
    {
        return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return null;
    }
}

Aqui está a mesma abordagem, contida em um método genérico:

public static T GetNewObject<T>()
{
    try
    {
        return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return default(T);
    }
}
tags2k
fonte
15
Programação orientada a exceções? Parece uma implementação muito ruim quando você pode simplesmente refletir sobre o tipo para determinar os construtores.
Firoso 22/03
16

É bem simples. Suponha que seu nome de classe seja Care o namespace seja Vehicles, depois passe o parâmetro como o Vehicles.Carque retorna o objeto do tipo Car. Assim, você pode criar qualquer instância de qualquer classe dinamicamente.

public object GetInstance(string strNamesapace)
{         
     Type t = Type.GetType(strNamesapace); 
     return  Activator.CreateInstance(t);         
}

Se o seu nome completo (ou seja, Vehicles.Carneste caso) estiver em outra montagem, o valor Type.GetTypeserá nulo. Nesses casos, você percorre todas as montagens e encontra o Type. Para isso você pode usar o código abaixo

public object GetInstance(string strFullyQualifiedName)
{
     Type type = Type.GetType(strFullyQualifiedName);
     if (type != null)
         return Activator.CreateInstance(type);
     foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
     {
         type = asm.GetType(strFullyQualifiedName);
         if (type != null)
             return Activator.CreateInstance(type);
     }
     return null;
 }

E você pode obter a instância chamando o método acima.

object objClassInstance = GetInstance("Vehicles.Car");
Sarath Avanavu
fonte
No seu segundo caso (montagem externa), você pode simplesmente passar "Vehicles.Car, OtherAssembly" para o seu primeiro método e ele funcionará. Obviamente OtherAssembly é o nome do conjunto em que vive.
danmiser
2
@danmiser Isso precisa codificar o nome do assembly. A fim de implementar a flexibilidade estou verificando nulo eo código funciona de forma dinâmica :)
Sarath Avanavu
14

Se isso é para algo que será chamado muito em uma instância de aplicativo, é muito mais rápido compilar e armazenar em cache o código dinâmico em vez de usar o ativador ou ConstructorInfo.Invoke(). Duas opções fáceis para compilação dinâmica são Linq Expressions compiladas ou alguns ILopcodes eDynamicMethod . De qualquer forma, a diferença é enorme quando você começa a fazer loops apertados ou várias chamadas.

Tom Mayfield
fonte
11

O genérico não T t = new T();funcionaria?

Brady Moritz
fonte
9
Na verdade, seria em uma classe / método genérico, mas não para um determinado "Tipo".
Brady Moritz
Supõe que o tipo T tenha a restrição 'new ()'.
Rob Von Nesselrode
10

Se você deseja usar o construtor padrão, a solução usada System.Activatoranteriormente é provavelmente a mais conveniente. No entanto, se o tipo não possuir um construtor padrão ou você precisar usar um não padrão, uma opção é usar reflexão ou System.ComponentModel.TypeDescriptor. Em caso de reflexão, basta saber apenas o nome do tipo (com seu espaço para nome).

Exemplo usando reflexão:

ObjectType instance = 
    (ObjectType)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(
        typeName: objectType.FulName, // string including namespace of the type
        ignoreCase: false,
        bindingAttr: BindingFlags.Default,
        binder: null,  // use default binder
        args: new object[] { args, to, constructor },
        culture: null, // use CultureInfo from current thread
        activationAttributes: null
    );

Exemplo usando TypeDescriptor:

ObjectType instance = 
    (ObjectType)System.ComponentModel.TypeDescriptor.CreateInstance(
        provider: null, // use standard type description provider, which uses reflection
        objectType: objectType,
        argTypes: new Type[] { types, of, args },
        args: new object[] { args, to, constructor }
    );
BSharp
fonte
args[]Foi exatamente o que cheguei a esta pergunta para encontrar, obrigado!
Chad
10

Sem uso de Reflexão:

private T Create<T>() where T : class, new()
{
    return new T();
}
magro
fonte
5
Como isso é útil? Você já deve saber o tipo para chamar esse método e, se souber o tipo, poderá construí-lo sem um método especial.
Kyle Delaney
Portanto, T pode variar em tempo de execução. Útil se você trabalha com tipos derivados.
um novo T (); falharia se T não fosse um tipo de referência com construtor sem parâmetros. Este método usa restrições para garantir que T seja do tipo referência e tenha um construtor.
3
Como T pode variar em tempo de execução? Você não precisa conhecer T em tempo de design para chamar Create <>?
Kyle Delaney
Se você trabalha com classes e interfaces genéricas em fábricas, os tipos que implementam a interface devem ser instanciados podem variar.
8

Dado esse problema, o Ativador funcionará quando houver um ctor sem parâmetros. Se for uma restrição, considere usar

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()
Thulani Chivandikwa
fonte
5
public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}
vikram nayak
fonte
4

Posso resolver essa questão porque estava procurando implementar um método CloneObject simples para classe arbitrária (com um construtor padrão)

Com o método genérico, você pode exigir que o tipo implemente New ().

Public Function CloneObject(Of T As New)(ByVal src As T) As T
    Dim result As T = Nothing
    Dim cloneable = TryCast(src, ICloneable)
    If cloneable IsNot Nothing Then
        result = cloneable.Clone()
    Else
        result = New T
        CopySimpleProperties(src, result, Nothing, "clone")
    End If
    Return result
End Function

Com não genérico, assuma que o tipo tem um construtor padrão e, se não o fizer, captura uma exceção.

Public Function CloneObject(ByVal src As Object) As Object
    Dim result As Object = Nothing
    Dim cloneable As ICloneable
    Try
        cloneable = TryCast(src, ICloneable)
        If cloneable IsNot Nothing Then
            result = cloneable.Clone()
        Else
            result = Activator.CreateInstance(src.GetType())
            CopySimpleProperties(src, result, Nothing, "clone")
        End If
    Catch ex As Exception
        Trace.WriteLine("!!! CloneObject(): " & ex.Message)
    End Try
    Return result
End Function
Darrel Lee
fonte