Bir Türden yeni bir nesne örneği nasıl oluşturulur


748

Bir Typenesne her zaman derleme zamanında bilmeyebilir , ancak bir örneğinin oluşturulması gerekebilir Type.

Bir nesneden nasıl yeni bir nesne örneği alırsınız Type?

Yanıtlar:


896

ActivatorKök içindeki sınıf Systemad oldukça güçlüdür.

Parametreleri yapıcıya ve benzerlerine aktarmak için çok fazla aşırı yük vardır. Şu adresteki belgelere göz atın:

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

veya (yeni yol)

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

İşte bazı basit örnekler:

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

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

21
Sonunda buldum sevindim, ancak ikinci çağrı tam olarak doğru değil, bir teklif eksik ve parms ters, olmalıdır: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespace.ObjectType");
kevinc

10
İstediğiniz gerçek nesne türünü almak için 'Unwrap ()' öğesini çağırmanız gerekir: ConcreteType örneği = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap ();
И Г И І И О

4
ObjectType instanceOP'nin "Bir kişi derleme zamanında bir nesnenin türünü her zaman bilemeyebilir" koşuluyla nasıl eşleşir? : P
Martin Schneider

@ MA-Maddin tamam o zaman object instance = Activator.CreateInstance(...);.
BrainSlugs83

1
Herkes bunu .NET Core'da nasıl biliyor? Paketten çıkarma yöntemi nesnede mevcut değil.
Justin

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

ActivatorSınıf bu biraz daha kolay hale getirir jenerik varyantı vardır:

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

8
@Kevin Elbette. Böyle bir operasyon olamaz o mantıklı değil, çünkü statik olarak yazılan bir dilde çalışır. Bilinmeyen türde bir nesne üzerinde yöntem çağıramazsınız. Bu arada (= bu cevabı yazdığından beri) C #, dynamicbu tür yapılara izin veren bir yapıya sahiptir , ancak çoğu amaç için bu cevap hala onu kapsamaktadır.
Konrad Rudolph

1
@KonradRudolph Çok doğru değil. C # İlk gelmez çalışma zamanında yeni türleri oluşturmak için izin verir. Onlara hiçbir şey statik olarak güvenli bir şekilde diyemezsiniz . Yani evet, yarı haklısın. Ancak daha gerçekçi olarak derlemeleri çalışma zamanında yüklediğinizde buna ihtiyacınız vardır, bu da türün derleme zamanında bilinmediği anlamına gelir. Bunu yapamazsanız C # ciddi şekilde sınırlı olacaktır. Demek istediğim, bunu kendiniz kanıtladınız: bir tür örnek alan Activator yöntemi başka nasıl çalışır? MS Activator sınıfını yazdığında, kullanıcıların yazacakları herhangi bir gelecek türü hakkında derleme zamanı bilgisi yoktu.
AnorZaken

1
@AnorZaken Yorumum, çalışma zamanında tür oluşturma hakkında hiçbir şey söylemiyor. Elbette bunu yapabilirsiniz, ancak bunları aynı bağlamda statik olarak kullanamazsınız (elbette statik olarak derlenmiş tam bir programa ev sahipliği yapabilirsiniz). Benim tek yorumum bu.
Konrad Rudolph

@KonradRudolph Ah üzgünüm, "böyle bir işlemi" yanlış yorumlayarak sadece çalışma zamanında bilinen bir türü örneklemek anlamına gelir; genel tür parametresi olarak bir çalışma zamanı türü kullanmak yerine.
AnorZaken

1
@AnorZaken - Teknik olarak hem statik güvenli bir şekilde üzerlerinde çalışma zamanı VE çağrı yöntemlerine yeni türleri oluşturabilir eğer yeni tip uygular bilinen bir arayüz veya devralır bilinen bir taban sınıfı. - Bu yaklaşımlardan herhangi biri, çalışma zamanı oluşturulan nesneniz için size statik bir sözleşme verecektir.
BrainSlugs83

132

Derlenmiş ifade en iyi yoldur! (performansın çalışma zamanında tekrar tekrar örnek oluşturması için).

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

X x = YCreator();

İstatistikler (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

İstatistikler (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

İstatistikler (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

İstatistikler (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

İstatistikler (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

İstatistikler (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

Tam kod:

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

18
Tüm istatistikler için +1! Şu anda bu tür bir performansa gerçekten ihtiyacım yok, ama yine de çok ilginç. :)
AnorZaken

1
Ayrıca TypeDescriptor.CreateInstance (bkz orada stackoverflow.com/a/17797389/1242 çabuk eğer TypeDescriptor.AddProvider ile kullanılabilir)
Lars Truijens

2
Çalışma Xzamanında ne tür olduğunu bilmediğinizde bu hala yararlı mı ?
ajeh

1
@ajeh Evet. (T) türünü Type.GetType (..) olarak değiştirin.
Serj-Tm

3
@ Serj-Tm Hayır, X tipi bir çalışma zamanı ise işe yaramaz Type.
NetMage

47

Bu sorunun bir uygulaması, Type'ın parametresiz yapıcısını çağırmayı denemektir:

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

İşte genel bir yöntemde yer alan aynı yaklaşım:

public static T GetNewObject<T>()
{
    try
    {
        return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return default(T);
    }
}

15
İstisna güdümlü programlama? Yapıcıları belirlemek için türün üzerine düşünebildiğinizde bu çok kötü bir uygulama gibi görünüyor.
Firoso

16

Oldukça basit. Sınıf adınızın Carve ad alanının olduğunu varsayalım Vehicles, ardından Vehicles.Cartürün nesnesini döndüren parametreyi iletin Car. Bunun gibi herhangi bir sınıfın dinamik bir şekilde herhangi bir örneğini oluşturabilirsiniz.

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

Senin Eğer Tamamen Nitelikli İsim (yani Vehicles.Carbu durumda) montaj diğerinde ise, Type.GetTypeboş olacaktır. Bu gibi durumlarda, tüm montajlar arasında döngü var ve bulmak Type. Bunun için aşağıdaki kodu kullanabilirsiniz

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

Ve yukarıdaki yöntemi çağırarak örneği alabilirsiniz.

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

İkinci durumda (harici montaj), sadece "Vehicles.Car, OtherAssembly" ı ilk yönteme geçirebilirsiniz ve çalışır. Açıkçası OtherAssembly içinde yaşadığı meclisin adıdır.
danmiser

2
@ danmiser Montaj adını kodlamak zor. Esnekliği uygulamak için null kontrol ediyorum ve kod dinamik bir şekilde çalışıyor :)
Sarath Avanavu

14

Bu, bir uygulama örneğinde çokça çağrılacak bir şeyse, etkinleştirici veya yerine dinamik kod derlemek ve önbelleğe almak çok daha hızlıdır ConstructorInfo.Invoke(). Dinamik derleme için iki kolay seçenek derlenmiş Linq Expressions veya bazı basit ILopcodes veDynamicMethod . Her iki durumda da, sıkı döngülere veya birden fazla çağrıya girmeye başladığınızda fark büyüktür.



10

Varsayılan kurucuyu kullanmak istiyorsanız, System.Activatordaha önce sunulan çözümü muhtemelen en uygun olanıdır. Ancak, türün varsayılan bir yapıcısı yoksa veya varsayılan olmayan bir yapıcı kullanmanız gerekiyorsa, yansıma veya seçeneğini kullanmaktır System.ComponentModel.TypeDescriptor. Yansıtma durumunda, sadece tür adını (ad alanı ile) bilmek yeterlidir.

Yansıma kullanma örneği:

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

Kullanarak örnek 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 }
    );

args[]tam olarak bu soruya bulmak için geldiğim şeydi, teşekkürler!
Çad

10

Yansıma kullanmadan:

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

5
Bu nasıl faydalı? Bu yöntemi çağırmak için zaten türü bilmeniz gerekir ve türü biliyorsanız, özel bir yöntem olmadan oluşturabilirsiniz.
Kyle Delaney

Böylece T çalışma zamanında değişebilir. Aşamalı Türlerle çalışıyorsanız kullanışlıdır.

yeni bir T (); T parametresiz yapıcıya sahip bir referans türü değilse başarısız olur. Bu yöntem, T'nin referans türü olduğundan ve bir yapıcıya sahip olduğundan emin olmak için karşıtlıklar kullanır.

3
T çalışma zamanında nasıl değişebilir? Create <> 'i aramak için tasarım zamanında T'yi bilmek zorunda değil misiniz?
Kyle Delaney

Fabrikalardaki genel sınıflar ve arabirimlerle çalışıyorsanız, arabirimi uygulayan türler örneklenmelidir.

8

Bu sorun göz önüne alındığında, aktivatör parametresiz bir ctor olduğunda çalışacaktır. Bu bir kısıtlama ise

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()

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

4

Ben rasgele sınıf (varsayılan bir kurucu ile) için basit bir CloneObject yöntemi uygulamak arıyordum çünkü bu soru karşısında olabilir

Genel yöntemle, türün New () uygulamasını gerektirebilir.

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

Generic olmayan tipte, varsayılan bir yapıcı olduğunu varsayar ve yoksa bir istisna yakalar.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.