2008-08-03 11 views
541

से एक नया ऑब्जेक्ट उदाहरण प्राप्त करें किसी को संकलन-समय पर ऑब्जेक्ट का प्रकार हमेशा नहीं पता हो सकता है, लेकिन टाइप का उदाहरण बनाने की आवश्यकता हो सकती है। आप एक प्रकार से एक नया ऑब्जेक्ट उदाहरण कैसे प्राप्त करते हैं?टाइप

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

यहाँ एक ही दृष्टिकोण, एक सामान्य विधि में निहित है:

उत्तर

685

रूट System नामस्थान के भीतर Activator कक्षा बहुत शक्तिशाली है।

कन्स्ट्रक्टर को पास करने वाले पैरामीटर के लिए बहुत सारे अधिभार हैं। पर दस्तावेज़ की जाँच करें:

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

या (नया पथ)

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

यहाँ कुछ सरल उदाहरण हैं:

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

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

अंत में यह पाया गया है, लेकिन दूसरा कॉल बिल्कुल सही नहीं है, एक उद्धरण और पैरामीटर उलटा हुआ होना चाहिए, ऑब्जेक्ट टाइप टाइप = (ऑब्जेक्ट टाइप) एक्टिवेटर। क्रिएट इंस्टेंस ("माईएस्प्लर", "माईनामस्पेस.ऑब्जेक्ट टाइप"); – kevinc

+8

आपको वास्तविक प्रकार की ऑब्जेक्ट प्राप्त करने के लिए 'अनवर्र()' को कॉल करने की आवश्यकता है: कंक्रीट टाइप उदाहरण = (कंक्रीट टाइप) एक्टिवेटर। क्रिएट इंस्टेंस (ऑब्जेक्ट टाइप)। अनवरप(); –

34

एक इस समस्या के कार्यान्वयन प्रकार के पैरामीटर कम निर्माता कॉल करने के लिए प्रयास करने के लिए है

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

अपवाद संचालित प्रोग्रामिंग का उपयोग करने पर विचार करें? यह बहुत खराब कार्यान्वयन की तरह लगता है जब आप रचनाकारों को निर्धारित करने के लिए बस प्रकार पर प्रतिबिंबित कर सकते हैं। – Firoso

11

यदि यह किसी ऐसी चीज के लिए है जिसे एप्लिकेशन इंस्टेंस में बहुत कुछ कहा जाएगा, तो यह सक्रियकर्ता या ConstructorInfo.Invoke() का उपयोग करने के बजाय गतिशील कोड को संकलित और कैश करने के लिए बहुत तेज़ है। गतिशील संकलन के लिए दो आसान विकल्प Linq Expressions या कुछ सरल IL opcodes and DynamicMethod संकलित किए गए हैं। किसी भी तरह से, जब आप तंग लूप या एकाधिक कॉल में शामिल होना शुरू करते हैं तो अंतर बहुत बड़ा होता है।

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

"आईएल opcodes और गतिशील विधि" लिंक मर चुका है। –

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

Activator वर्ग एक सामान्य संस्करण इस थोड़ा आसान बनाता है?

+9

इसके अलावा रनटाइम 'टाइप टी' के लिए काम नहीं करता है। –

+3

@ केविन निश्चित रूप से। ऐसा ऑपरेशन * एक स्थिर रूप से टाइप की गई भाषा में काम नहीं कर सकता क्योंकि यह समझ में नहीं आता है। आप अज्ञात प्रकार की वस्तु पर विधियों का आह्वान नहीं कर सकते हैं।इस बीच (= इस उत्तर को लिखने के बाद) सी # को 'गतिशील' निर्माण मिला है जो * इस तरह की संरचनाओं को अनुमति देता है लेकिन अधिकांश उद्देश्यों के लिए यह उत्तर अभी भी इसे कवर करता है। –

+0

@ कोनराड्रूडॉल्फ बिल्कुल सही नहीं है। सी # * के पहले * आपको रनटाइम पर नए प्रकार बनाने की अनुमति देता है। आप उन पर कुछ भी नहीं कह सकते * एक स्थिर रूप से सुरक्षित तरीके से *। तो हाँ, आप आधा सही हैं। लेकिन अधिक यथार्थवादी रूप से आपको इसकी आवश्यकता होती है जब आप रनटाइम पर असेंबली लोड करते हैं, जिसका अर्थ है कि प्रकार संकलन समय पर ज्ञात नहीं है। यदि आप ऐसा नहीं कर पा रहे हैं तो सी # गंभीर रूप से सीमित होगा। मेरा मतलब है कि आपने इसे स्वयं साबित कर दिया है: एक्टिवेटर विधि किस प्रकार एक प्रकार-उदाहरण काम करती है? जब एमएस ने एक्टिवेटर क्लास लिखा था तो उनके पास लिखने वाले किसी भी भविष्य के प्रकार का संकलन-समय ज्ञान नहीं था। – AnorZaken

7

चाहेंगे नहीं सामान्य T t = new T(); काम:

+7

वास्तव में, यह एक सामान्य वर्ग/विधि में होगा, लेकिन किसी दिए गए "प्रकार" के लिए नहीं। –

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

आप डिफ़ॉल्ट निर्माता का उपयोग करना चाहते हैं तो System.Activator पहले प्रस्तुत का उपयोग कर समाधान शायद सबसे सुविधाजनक है। हालांकि, यदि प्रकार में डिफॉल्ट कन्स्ट्रक्टर की कमी है या आपको एक गैर-डिफ़ॉल्ट का उपयोग करना है, तो प्रतिबिंब या System.ComponentModel.TypeDescriptor का उपयोग करना एक विकल्प है। प्रतिबिंब के मामले में, केवल प्रकार का नाम जानने के लिए पर्याप्त है (इसके नामस्थान के साथ)। प्रतिबिंब का उपयोग

उदाहरण:

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

उदाहरण 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 } 
    ); 
9

इसका बहुत सरल। मान लें कि आपका वर्गनाम Car है और नामस्थान Vehicles है, तो पैरामीटर को Vehicles.Car के रूप में पास करें जो Car प्रकार का ऑब्जेक्ट देता है। इस तरह आप गतिशील रूप से किसी भी वर्ग का कोई उदाहरण बना सकते हैं।

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

यदि आपका Fully Qualified Name (यानी, इस मामले में Vehicles.Car) एक और विधानसभा में है, Type.GetType अशक्त हो जाएगा। ऐसे मामलों में, आपके पास सभी असेंबली के माध्यम से लूप है और Type खोजें। इसके लिए आप नीचे दिए गए कोड

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

का उपयोग कर सकते हैं और आप उपर्युक्त विधि को कॉल करके उदाहरण प्राप्त कर सकते हैं।

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

आपके दूसरे मामले (बाहरी असेंबली) में, आप बस अपनी पहली विधि में "वाहन। कार, अन्य एस्सेप्लर" में जा सकते हैं और यह काम करेगा। जाहिर है अन्य एस्प्लोर्स उस असेंबली का नाम है जिसमें यह रहता है। – danmiser

+1

@ डैनमिसर जिसे असेंबली नाम को कड़ी कोडिंग की आवश्यकता है। लचीलापन लागू करने के लिए मैं शून्य की जांच कर रहा हूं और कोड गतिशील तरीके से काम करता है :) –

3

मैं इस सवाल क्योंकि मैं (एक डिफ़ॉल्ट निर्माता के साथ) मनमाने ढंग से वर्ग के लिए एक सरल CloneObject विधि लागू करने के लिए देख रहा था

सामान्य विधि के साथ भर में आप की आवश्यकता कर सकते हैं कर सकते हैं प्रकार लागू करता है कि नई()।

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 
गैर सामान्य मान प्रकार एक डिफ़ॉल्ट निर्माता है और पकड़ने के लिए एक अपवाद है, तो यह नहीं है के साथ

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 
73

संकलित अभिव्यक्ति सबसे अच्छा तरीका है! (रनटाइम में बार-बार उदाहरण बनाने के लिए प्रदर्शन के लिए)।

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

X x = YCreator(); 

सांख्यिकी (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 

सांख्यिकी (2015, .net 4.5, 64):

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 

सांख्यिकी (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 

सांख्यिकी (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 

पूर्ण कोड:

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

+1 सभी आंकड़ों के लिए! मुझे इस समय इस तरह के प्रदर्शन की ज़रूरत नहीं है, लेकिन अभी भी बहुत दिलचस्प है। :) – AnorZaken

+0

इसके अलावा TypeDescriptor.CreateInstance (http://stackoverflow.com/a/17797389/1242 देखें) जो TypeDescriptor.AddProvider –

+1

के साथ उपयोग किए जाने पर तेज़ हो सकता है यह तब भी उपयोगी होता है जब आप नहीं जानते कि 'X' रनटाइम पर है? – ajeh

8

प्रतिबिंब के उपयोग के बिना:

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

यह कैसे उपयोगी है? आपको उस विधि को कॉल करने के लिए पहले से ही टाइप करना होगा, और यदि आप उस प्रकार को जानते हैं जिसे आप बिना किसी विशेष विधि के बना सकते हैं। –

+0

तो टी रनटाइम पर भिन्न हो सकता है। उपयोगी अगर आप व्युत्पन्न प्रकार के साथ काम करते हैं। –

+0

एक नया टी(); अगर टी पैरामीटर रहित कन्स्ट्रक्टर के साथ संदर्भ प्रकार नहीं है तो विफल हो जाएगा, यह विधियां टी संदर्भ संदर्भ प्रकार सुनिश्चित करने के लिए बाधाओं का उपयोग करती हैं और इसमें एक कन्स्ट्रक्टर है। –

5

इस समस्या है जब वहाँ एक parameterless ctor है उत्प्रेरक काम करेंगे को देखते हुए। यदि यह एक बाधा है

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject() 
संबंधित मुद्दे