Yansıma kullanarak C # 'da varsayılan kurucu olmadan tür örneği oluşturma


98

Aşağıdaki dersi örnek olarak alın:

class Sometype
{
    int someValue;

    public Sometype(int someValue)
    {
        this.someValue = someValue;
    }
}

Daha sonra yansımayı kullanarak bu türden bir örnek oluşturmak istiyorum:

Type t = typeof(Sometype);
object o = Activator.CreateInstance(t);

Normalde bu işe yarar, ancak SomeTypeparametresiz bir kurucu tanımlamadığından, çağrısı " Bu nesne için parametresiz kurucu tanımlanmadı " mesajıyla bir Activator.CreateInstancetür istisnası atacaktır . Bu türden bir örnek oluşturmanın hala alternatif bir yolu var mı? Tüm sınıflarıma parametresiz kurucular eklemek biraz berbat olurdu.MissingMethodException


2
FormatterServices.GetUninitializedObjectbaşlatılmamış dizge oluşturmaya izin verilmez. İstisna alabilirsiniz: System.ArgumentException: Uninitialized Strings cannot be created.Lütfen bunu aklınızda bulundurun.
Bartosz Pierzchlewicz

Uyarılar için teşekkürler, ama zaten dizeleri ve temel türleri ayrı ayrı ele alıyorum.
Aistina

Yanıtlar:


143

Başlangıçta bu cevabı burada yayınladım , ancak burada bir yeniden baskı var, çünkü bu aynı soru değil ama aynı cevaba sahip:

FormatterServices.GetUninitializedObject()yapıcı çağırmadan bir örnek oluşturacaktır. Bu sınıfı Reflector kullanarak ve bazı çekirdek .Net serileştirme sınıflarını araştırarak buldum .

Aşağıdaki örnek kodu kullanarak test ettim ve harika çalışıyor gibi görünüyor:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;

namespace NoConstructorThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass)); //does not call ctor
            myClass.One = 1;
            Console.WriteLine(myClass.One); //write "1"
            Console.ReadKey();
        }
    }

    public class MyClass
    {
        public MyClass()
        {
            Console.WriteLine("MyClass ctor called.");
        }

        public int One
        {
            get;
            set;
        }
    }
}

Harika, tam da ihtiyacım olan şey gibi görünüyor. Başlatılmamış, tüm hafızasının sıfırlara ayarlanacağı anlamına mı geliyor? (
Yapıların

Her tür için varsayılan değer ne olursa olsun, varsayılan değer olacaktır. Yani nesneler null olacak, tam sayılar 0, vb. Herhangi bir sınıf düzeyinde başlatma gerçekleştiğini düşünüyorum, ancak hiçbir kurucu çalıştırılmıyor.
Jason Jackson

14
@JSBangs, Bu tamamen meşru bir cevap alıyorsun. Yorumunuz ve diğer cevap aslında sorulan soruyu ele almıyor. Daha iyi bir cevabınız olduğunu düşünüyorsanız, bir tane verin. Ancak verdiğim cevap, belgelenmiş bir sınıfın, diğer serileştirme sınıflarının bu kodu kullandığı şekilde nasıl kullanılacağını vurgulamaktadır.
Jason Jackson

21
@JSBangs FormatterServices ( msdn.microsoft.com/en-us/library/… ) belgelenmemiş.
Autodidact


72

CreateInstance yönteminin bu aşırı yüklemesini kullanın:

public static Object CreateInstance(
    Type type,
    params Object[] args
)

Belirtilen parametrelerle en iyi eşleşen yapıcıyı kullanarak belirtilen türün bir örneğini oluşturur.

Bakınız: http://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx


1
Bu çözüm, sorunu fazlasıyla basitleştirir. Ya türümü bilmiyorsam ve "sadece bu Tür değişkeninde Türden bir nesne oluştur" diyorsam?
kamii

23

Ne zaman hedeflendiği performansı (T)FormatterServices.GetUninitializedObject(typeof(T))ona yavaştı. Aynı zamanda derlenmiş ifadeler, yalnızca varsayılan kurucuya sahip türler için çalışsalar da size büyük hız iyileştirmeleri sağlayacaktır. Hibrit bir yaklaşım aldım:

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

Bu, oluşturma ifadesinin etkin bir şekilde önbelleğe alındığı ve yalnızca tür ilk yüklendiğinde cezaya neden olduğu anlamına gelir. Değer türlerini de verimli bir şekilde ele alacak.

Bunu aramak:

MyType me = New<MyType>.Instance();

(T)FormatterServices.GetUninitializedObject(t)Bunun string için başarısız olacağını unutmayın . Dolayısıyla, boş dizge döndürmek için dizge için özel bir işlem yapılır.


1
Birinin kodunun bir satırına bakmanın bir günü kurtarması garip. Teşekkürler bayım! Performans nedenleri beni gönderinize götürdü ve hile bitti :) FormatterServices ve Activator sınıfları, derlenmiş ifadelere kıyasla daha düşük performans gösteriyor, biri her yerde Aktivatörleri bulması ne yazık.
jmodrak

@nawfal String için özel işlemenizle ilgili olarak, bu özel işlem olmadan string için başarısız olacağını biliyorum, ancak sadece bilmek istiyorum: diğer tüm türler için çalışacak mı?
Sнаđошƒаӽ

@ Sнаđошƒаӽ maalesef hayır. Verilen örnek barebone'lardır ve .NET'in birçok farklı türü vardır. Örneğin, bir temsilci tipini geçerseniz, size nasıl bir örnek vereceğinizi düşünün. Ya da kurucu atarsa ​​bununla ilgili ne yapabilirsiniz? Bununla başa çıkmanın birçok farklı yolu. Kitaplığımda çok daha fazla senaryoyu işlemek için bu güncellemeyi yanıtladığımdan beri vardı. Şimdilik hiçbir yerde yayınlanmadı.
nawfal

4

İyi yanıtlar ancak dot net kompakt çerçevede kullanılamaz. İşte CF.Net üzerinde çalışacak bir çözüm ...

class Test
{
    int _myInt;

    public Test(int myInt)
    {
        _myInt = myInt;
    }

    public override string ToString()
    {
        return "My int = " + _myInt.ToString();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var ctor = typeof(Test).GetConstructor(new Type[] { typeof(int) });
        var obj = ctor.Invoke(new object[] { 10 });
        Console.WriteLine(obj);
    }
}

1
Varsayılan olmayan bir kurucu çağırmanın yolu budur. Herhangi bir kurucu çağırmadan bir nesne oluşturmak isteyeceğimden emin değilim.
Rory MacLeod

2
Özel serileştiriciler yazıyorsanız, yapıcıları çağırmadan bir nesne oluşturmak isteyebilirsiniz.
Autodidact

1
Evet, bu sorunun tam olarak kullanım senaryosu bu :)
Aistina

1
@Aistina Belki bu bilgiyi soruya ekleyebilirsin? Çoğu insan, ctorlerini çağırmadan nesneler yaratmaya karşı çıkar ve bu konuda sizinle tartışmak için zaman ayırır, ancak kullanım durumunuz aslında bunu haklı çıkarır, bu yüzden sorunun kendisi ile çok alakalı olduğunu düşünüyorum.
julealgon
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.