Ottenere una nuova istanza di oggetto da un tipo

voti
554

Non si può sempre conoscere il tipo di un oggetto al momento della compilazione, ma potrebbe essere necessario creare un'istanza del tipo. Come si ottiene una nuova istanza di oggetto da un tipo?

È pubblicato 03/08/2008 alle 17:29
fonte dall'utente
In altre lingue...                            


12 risposte

voti
35

Un'implementazione di questo problema è quello di tentare di chiamare il costruttore senza parametri del tipo:

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

Ecco lo stesso approccio, contenuto in un metodo generico:

public static T GetNewObject<T>()
{
    try
    {
        return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return default(T);
    }
}
Risposto il 03/08/2008 a 17:31
fonte dall'utente

voti
709

La Activatorclasse all'interno della radice Systemdello spazio dei nomi è abbastanza potente.

Ci sono un sacco di sovraccarichi per passare parametri al costruttore e così via. Controlla la documentazione all'indirizzo:

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

o (nuovo percorso)

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

Ecco alcuni semplici esempi:

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

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");
Risposto il 03/08/2008 a 17:35
fonte dall'utente

voti
11

Se questo è per qualcosa che sarà chiamato un sacco in un'istanza di applicazione, è molto più veloce per compilare e codice dinamico della cache invece di utilizzare l'attivatore o ConstructorInfo.Invoke(). Due semplici opzioni per la compilazione dinamica vengono compilati Linq espressioni o alcuni semplici ILcodici operativi eDynamicMethod . In entrambi i casi, la differenza è enorme quando si inizia a entrare in cicli stretti o chiamate multiple.

Risposto il 25/08/2008 a 14:31
fonte dall'utente

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

La Activatorclasse ha una variante generica che rende questo un po 'più facile:

ObjectType instance = Activator.CreateInstance<ObjectType>();
Risposto il 25/08/2008 a 14:33
fonte dall'utente

voti
7

Non sarebbe il generico T t = new T();lavoro?

Risposto il 17/08/2010 a 15:30
fonte dall'utente

voti
3
public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}
Risposto il 10/09/2012 a 00:08
fonte dall'utente

voti
7

Se si desidera utilizzare il costruttore di default, allora la soluzione con System.Activatorpresentato in precedenza è probabilmente il più conveniente. Tuttavia, se il tipo manca un costruttore di default o si deve utilizzare uno non predefinito, quindi una possibilità è quella di utilizzare la riflessione o System.ComponentModel.TypeDescriptor. In caso di riflessione, è sufficiente sapere solo il nome del tipo (con il suo spazio dei nomi).

Esempio con riflessione:

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

Esempio con 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 }
    );
Risposto il 22/07/2013 a 22:03
fonte dall'utente

voti
9

La sua piuttosto semplice. Si supponga che il nome classe è Care lo spazio dei nomi è Vehicles, quindi passare il parametro come Vehicles.Carche restituisce oggetto di tipo Car. Ti piace questa è possibile creare qualsiasi istanza di qualsiasi classe in modo dinamico.

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

Se il vostro nome Fully Qualified (vale a dire, Vehicles.Carin questo caso) è in un altro assembly, il Type.GetTypesarà nullo. In questi casi, si deve scorrere tutti i gruppi e trovare il Type. Per questo è possibile utilizzare il codice qui sotto

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 si può ottenere l'istanza chiamando il metodo di cui sopra.

object objClassInstance = GetInstance("Vehicles.Car");
Risposto il 03/11/2014 a 06:11
fonte dall'utente

voti
3

Posso in tutta questa domanda perché stavo cercando di implementare un metodo semplice per la classe CLONEOBJECT arbitraria (con un costruttore di default)

Con metodo generico si può esigere che il tipo implementa Nuovo ().

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

Con non generico assumere il tipo ha un costruttore di default e prendere un'eccezione se non è così.

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
Risposto il 24/03/2015 a 18:10
fonte dall'utente

voti
78

espressione compilato è modo migliore! (Per le prestazioni a volte creare l'istanza in fase di esecuzione).

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

X x = YCreator();

Statistiche (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

Statistiche (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

Statistiche (2015, .net 4.5, 86):

    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

Statistiche (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

codice 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) {}
}
Risposto il 30/04/2015 a 16:13
fonte dall'utente

voti
8

Senza l'uso di riflessione:

private T Create<T>() where T : class, new()
{
    return new T();
}
Risposto il 30/06/2015 a 12:51
fonte dall'utente

voti
5

Dato questo problema l'Activator funziona quando c'è una ctor senza parametri. Se questo è un vincolo considerare l'utilizzo

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()
Risposto il 30/06/2015 a 16:35
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more