Cum se creează o nouă instanță de obiect dintr-un tip (Programare, C#,.Net, Performanță, Reflecție, Tipuri)

tags2k a intrebat.

Este posibil să nu se știe întotdeauna care este Type al unui obiect la compilare, dar poate fi nevoie să se creeze o instanță de tip Type.

Cum se obține o nouă instanță de obiect dintr-un tip Type?

12 răspunsuri
Karl Seguin

Activator clasa din cadrul rădăcinii System este destul de puternică.

Există o mulțime de supraîncărcări pentru trecerea parametrilor către constructor și altele. Consultați documentația la:

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

sau (noua cale)

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

Iată câteva exemple simple:

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

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

Comentarii

    22

  • Mă bucur că am găsit în sfârșit acest lucru, dar al doilea apel nu este chiar corect, lipsește un ghilimele și parms inversat, ar trebui să fie: ObjectType instance = (ObjectType)Activator.CreateInstance(„MyAssembly”, „MyNamespace.ObjectType”); –  > Por kevinc.
  • Trebuie să apelați „Unwrap()” pentru a obține tipul real de obiect dorit: ConcreteType instance = (ConcreteType)Activator.CreateInstance(objectType).Unwrap(); – –  > Por Ε Г И І И О.
  • Cum se face ObjectType instance se potrivește cu condiția lui OP „One may not always know the type of an object at compile-time” ? 😛 – –  > Por Martin Schneider.
  • @MA-Maddin în regulă atunci, object instance = Activator.CreateInstance(...);. –  > Por BrainSlugs83.
  • Știe cineva cum să facă acest lucru în .NET Core? Metoda Unwrap nu este disponibilă pe obiect. –  > Por Justin.
Konrad Rudolph
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

The Activator are o variantă generică care ușurează puțin acest lucru:

ObjectType instance = Activator.CreateInstance<ObjectType>();

Comentarii

  • @Kevin Bineînțeles. O astfel de operațiune nu poate funcționa într-un limbaj static tipizat pentru că nu are sens. Nu puteți invoca metode pe un obiect de tip necunoscut. Între timp (= de când am scris acest răspuns), C# a obținut dynamic construct care face permite astfel de construcții, dar pentru majoritatea scopurilor acest răspuns încă acoperă acest lucru. –  > Por Konrad Rudolph.
  • @KonradRudolph Nu este chiar adevărat. În primul rând de c# face vă permite să creați noi tipuri în timpul execuției. Doar că nu poți apela nimic pe ele într-un mod static sigur. Deci, da, ai dreptate pe jumătate. Dar, în mod realist, aveți nevoie de acest lucru atunci când încărcați ansambluri în timpul execuției, ceea ce înseamnă că tipul nu este cunoscut la compilare. C# ar fi extrem de limitat dacă nu ai putea face acest lucru. Adică tocmai ați dovedit-o dumneavoastră: cum altfel funcționează metoda Activator care ia o instanță de tip? Atunci când MS a scris clasa Activator, nu a avut cunoștință la compilare de niciun tip pe care utilizatorii îl vor scrie în viitor. –  > Por AnorZaken.
  • @AnorZaken Comentariul meu nu spune nimic despre crearea de tipuri în timpul execuției. Bineînțeles că poți face asta, dar nu le poți folosi static în același context (puteți găzdui un program complet compilat static, desigur). Asta este tot ce spune comentariul meu. –  > Por Konrad Rudolph.
  • @KonradRudolph Ah, îmi pare rău, am interpretat greșit „o astfel de operațiune” ca însemnând instanțierea unui tip care este cunoscut doar la execuție; în loc să însemne utilizarea unui tip la execuție ca parametru de tip generic. –  > Por AnorZaken.
  • @AnorZaken — din punct de vedere tehnic, puteți crea noi tipuri la momentul execuției ȘI puteți apela metode asupra lor într-un mod static sigur. if noul tip implementează o interfață cunoscută sau moștenește o clasă de bază cunoscută. — Oricare dintre aceste abordări vă va oferi un contract static pentru obiectul creat în timpul execuției. –  > Por BrainSlugs83.
Serj-Tm

Expresia compilată este cea mai bună cale! (pentru performanță, pentru a crea în mod repetat o instanță în timp de execuție).

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

X x = YCreator();

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

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

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

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

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

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

Cod complet:

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) {}
}

Comentarii

    20

  • +1 pentru toate statisticile! Nu prea am nevoie de acest tip de performanță în acest moment, dar totuși foarte interesant. 🙂 –  > Por AnorZaken.
  • De asemenea, există TypeDescriptor.CreateInstance (vezi stackoverflow.com/a/17797389/1242) care poate fi mai rapid dacă este folosit cu TypeDescriptor.AddProvider –  > Por Lars Truijens.
  • Este acest lucru încă util atunci când nu știi ce tip X este la momentul execuției? –  > Por ajeh.
  • @ajeh Da. Schimbați typeof(T) în Type.GetType(…). –  > Por Serj-Tm.
  • @Serj-Tm Nu, asta nu va funcționa dacă tipul X este un timp de execuție. Type. –  > Por NetMage.
Sarath KS

Este destul de simplu. Să presupunem că numele clasei dvs. este Car și spațiul de nume este Vehicles, apoi treceți parametrul ca Vehicles.Car care returnează un obiect de tip Car. În acest fel, puteți crea în mod dinamic orice instanță a oricărei clase.

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

În cazul în care Nume complet calificat(adică, Vehicles.Car în acest caz) se află într-un alt ansamblu, se va aplica Type.GetType va fi nulă. În astfel de cazuri, trebuie să parcurgeți în buclă toate ansamblurile și să găsiți Type. În acest scop, puteți utiliza codul de mai jos

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

Și puteți obține instanța prin apelarea metodei de mai sus.

object objClassInstance = GetInstance("Vehicles.Car");

Comentarii

  • În cel de-al doilea caz (ansamblu extern), puteți trece „Vehicles.Car,OtherAssembly” în prima metodă și va funcționa. Evident, OtherAssembly este numele ansamblului în care se află. –  > Por danmiser.
  • @danmiser Asta are nevoie de codificarea dură a numelui de asamblare. Pentru a implementa flexibilitatea, verific null și codul funcționează în mod dinamic 🙂 –  > Por Sarath KS.
Tom Mayfield

Dacă acest lucru este pentru ceva care va fi apelat de multe ori într-o instanță de aplicație, este mult mai rapid să compilați și să puneți în cache codul dinamic în loc să folosiți activatorul sau ConstructorInfo.Invoke(). Două opțiuni simple pentru compilarea dinamică sunt compilate Expresii Linq sau o simplă IL opcodes și DynamicMethod. Oricum ar fi, diferența este uriașă atunci când începeți să intrați în bucle strânse sau în apeluri multiple.

utilizator887983

Fără utilizarea Reflection:

private T Create<T>() where T : class, new()
{
    return new T();
}

Comentarii

  • Cum este util acest lucru? Trebuie să cunoști deja tipul pentru a apela acea metodă, iar dacă știi tipul, îl poți construi fără o metodă specială. –  > Por Kyle Delaney.
  • Deci T poate varia în timpul execuției. Este util dacă lucrați cu tipuri derivate. – user887983
  • a new T(); ar eșua dacă T nu este un tip de referință cu un constructor fără parametru, Această metodă utilizează constrângeri pentru a se asigura că T este un tip de referință și are un constructor. – user887983
  • Cum poate varia T în timpul execuției? Nu trebuie să cunoașteți T în momentul proiectării pentru a putea apela Create <>? –  > Por Kyle Delaney.
  • Dacă lucrați cu clase și interfețe generice în fabrici, tipurile care implementează interfața ar trebui să fie instanțiate pot varia. – utilizator887983
BSharp

Dacă doriți să utilizați constructorul implicit, atunci soluția care utilizează System.Activator prezentată anterior este probabil cea mai convenabilă. Cu toate acestea, dacă tipul nu are un constructor implicit sau dacă trebuie să utilizați unul care nu este implicit, atunci o opțiune este să utilizați reflecția sau System.ComponentModel.TypeDescriptor. În cazul reflectării, este suficient să se cunoască doar numele tipului (cu spațiul său de nume).

Exemplu de utilizare a reflecției:

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

Exemplu de utilizare a reflecției 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 }
    );

Comentarii

  • args[] a fost exact ceea ce am venit să găsesc la această întrebare, mulțumesc! –  > Por Chad.
Brady Moritz

Nu ar trebui ca genericul T t = new T(); funcționa?

Comentarii

  • De fapt, ar funcționa într-o clasă/metodă generică, dar nu și pentru un anumit „Tip”. –  > Por Brady Moritz.
  • Presupunem că tipul T are constrângerea „new()”. –  > Por Rob Von Nesselrode.
Thulani Chivandikwa

Având în vedere această problemă, Activator va funcționa atunci când există un ctor fără parametri. Dacă aceasta este o constrângere, luați în considerare utilizarea

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()

vikram nayak
public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}

Darrel Lee

Pot să trec peste această întrebare, deoarece căutam să implementez o metodă simplă CloneObject pentru o clasă arbitrară (cu un constructor implicit)

Cu o metodă generică puteți cere ca tipul să implementeze 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

Cu o metodă non-generică, se presupune că tipul are un constructor implicit și se prinde o excepție dacă nu are.

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

Nathan Evans

Doar ca o completare pentru oricine folosește răspunsurile de mai sus care implementează:

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

Aveți grijă – dacă constructorul dvs. nu este „Public”, veți primi următoarea eroare:

„System.MissingMethodException: ‘No parameterless constructor definedfor this object.”

Clasa dvs. poate fi Internal/Friend, sau orice aveți nevoie, dar constructorul trebuie să fie public.