2009-04-26 17 views
20

मैं कक्षा संदर्भ का उपयोग कर किसी ऑब्जेक्ट का उदाहरण कैसे बना सकता हूं, और यह सुनिश्चित करता है कि निर्माता को निष्पादित किया गया हो?मैं क्लास संदर्भ से डेल्फी ऑब्जेक्ट कैसे बना सकता हूं और कन्स्ट्रक्टर निष्पादन सुनिश्चित कर सकता हूं?

इस कोड उदाहरण में, TMyClass के निर्माता कहा जाता है नहीं किया जाएगा:

type 
    TMyClass = class(TObject) 
    MyStrings: TStrings; 
    constructor Create; virtual; 
    end; 

constructor TMyClass.Create; 
begin 
    MyStrings := TStringList.Create; 
end; 

procedure Test; 
var 
    Clazz: TClass; 
    Instance: TObject; 
begin 
    Clazz := TMyClass; 
    Instance := Clazz.Create; 
end; 

उत्तर

23

उपयोग करें:

type 
    TMyClass = class(TObject) 
    MyStrings: TStrings; 
    constructor Create; virtual; 
    end; 
    TMyClassClass = class of TMyClass; // <- add this definition 

constructor TMyClass.Create; 
begin 
    MyStrings := TStringList.Create; 
end; 

procedure Test; 
var 
    Clazz: TMyClassClass; // <- change TClass to TMyClassClass 
    Instance: TObject; 
begin 
    Clazz := TMyClass; // <- you can use TMyClass or any of its child classes. 
    Instance := Clazz.Create; // <- virtual constructor will be used 
end; 

वैकल्पिक रूप से, आप एक टाइप-कास्ट्स को टीएम क्लास ("टीएम क्लास की कक्षा" के बजाय) का उपयोग कर सकते हैं।

+1

ठीक है, अगर मैं सही ढंग से समझता हूं तो इसका मतलब है कि अगर मैं डेल्फी के साथ एक सामान्य ऑब्जेक्ट फैक्ट्री बनाना चाहता हूं, तो मुझे एक चर के लिए "टीएम क्लास की कक्षा" असाइन करना होगा - लेकिन ऐसा संभव नहीं है। – mjn

+1

यदि आप किसी प्रकार के ऑब्जेक्ट को बनाना चाहते हैं, तो आपको कक्षा प्रकार की जानकारी रखने की आवश्यकता है। यदि आपके पास कोई कक्षा की जानकारी नहीं है - तो आप इस प्रकार की वस्तु का निर्माण नहीं कर सकते हैं। काफी स्पष्ट;) – Alex

6

आपका कोड थोड़ा संशोधित:

type 
    TMyObject = class(TObject) 
    MyStrings: TStrings; 
    constructor Create; virtual; 
    end; 
    TMyClass = class of TMyObject; 

constructor TMyObject.Create; 
begin 
    inherited Create; 
    MyStrings := TStringList.Create; 
end; 

procedure Test; 
var 
    C: TMyClass; 
    Instance: TObject; 
begin 
    C := TMyObject; 
    Instance := C.Create; 
end; 
10

कृपया जांचें कि ओवरकॉइडिंग एक विकल्प है या नहीं।

+1

बहुत अच्छा विचार, यह TObject में एक वर्चुअल विधि है इसलिए मुझे कोई सिंथेटिक नई रूट क्लास जोड़ने की आवश्यकता नहीं है। इस विचार के लिए +1। – mjn

+0

वास्तव में बहुत उपयोगी! –

18

अलेक्जेंडर का समाधान एक अच्छा है लेकिन कुछ स्थितियों में पर्याप्त नहीं है। मान लीजिए कि आप एक TClassFactory क्लास स्थापित करना चाहते हैं जहां TClass संदर्भ रनटाइम के दौरान संग्रहीत किए जा सकते हैं और बाद में पुनर्प्राप्त उदाहरणों की मनमानी संख्या।

इस तरह के एक वर्ग कारखाने को कक्षाओं के वास्तविक प्रकारों के बारे में कभी भी कुछ नहीं पता होगा और इस प्रकार उन्हें मेटा वर्गों के अनुसार नहीं डाला जा सकता है। ऐसे मामलों में सही रचनाकारों का आह्वान करने के लिए, निम्नलिखित दृष्टिकोण काम करेगा।

सबसे पहले, हमें एक साधारण डेमो वर्ग की आवश्यकता है (सार्वजनिक क्षेत्रों को ध्यान में रखें, यह सिर्फ प्रदर्शन उद्देश्यों के लिए है)।

interface 

uses 
    RTTI; 

type 
    THuman = class(TObject) 
    public 
    Name: string; 
    Age: Integer; 

    constructor Create(); virtual; 
    end; 

implementation 

constructor THuman.Create(); 
begin 
    Name:= 'John Doe'; 
    Age:= -1; 
end; 

अब हम पूरी तरह से आरटीटीआई द्वारा और सही कन्स्ट्रक्टर कॉल के साथ थुमान प्रकार की वस्तु को तुरंत चालू करते हैं।

procedure CreateInstance(); 
var 
    someclass: TClass; 
    c: TRttiContext; 
    t: TRttiType; 
    v: TValue; 
    human1, human2, human3: THuman; 
begin 
    someclass:= THuman; 

    // Invoke RTTI 
    c:= TRttiContext.Create; 
    t:= c.GetType(someclass); 

    // Variant 1a - instantiates a THuman object but calls constructor of TObject 
    human1:= t.AsInstance.MetaclassType.Create; 

    // Variant 1b - same result as 1a 
    human2:= THuman(someclass.Create); 

    // Variant 2 - works fine 
    v:= t.GetMethod('Create').Invoke(t.AsInstance.MetaclassType,[]); 
    human3:= THuman(v.AsObject); 

    // free RttiContext record (see text below) and the rest 
    c.Free; 

    human1.Destroy; 
    human2.Destroy; 
    human3.Destroy; 
end; 

आप पाएंगे कि वस्तुओं "human1" और "human2", शून्य करने के लिए प्रारंभ किया गया है जैसे कि, नाम = '' और उम्र = 0, जो नहीं है कि हम क्या चाहते हैं। वस्तु मानव 3 के बजाय थूमान के निर्माता में प्रदान किए गए डिफ़ॉल्ट मान रखती है।

नोट, हालांकि, इस विधि के लिए आपके वर्गों को पैरामीटर के साथ कन्स्ट्रक्टर विधियों की आवश्यकता होती है। उपरोक्त सभी को मेरे द्वारा कल्पना नहीं की गई थी, लेकिन Rob Love's Tech Corner में शानदार ढंग से और अधिक विस्तार से (उदाहरण के लिए, सी। फ्री भाग) समझाया गया था।

0

आप बेस क्लास में एक सार विधि बना सकते हैं, और इसे कन्स्ट्रक्टर में कॉल कर सकते हैं, और क्लास रेफरेंस से बनाए जाने पर बाल कक्षाओं में ओवरराइड कर सकते हैं।

+0

यह वही नहीं है जो सवाल पूछ रहा है। – bummi

+0

मुझे इस समस्या का सामना करना पड़ा, जब मैं कुछ सूची प्रबंधक के निर्माता में कक्षा संदर्भ पास करता हूं, यह निर्दिष्ट करता है कि यह किस प्रकार का बच्चा सूची बना रहा था, और जब सूची ने बच्चों को बनाया, केवल बच्चों के नियंत्रक के मूल आधार वर्ग कहा जाता था ... बच्चों के सामान का निर्माता नहीं था ...मेरे लिए एकमात्र समाधान, क्योंकि कन्स्ट्रक्टर के पास पैरामीटर होना चाहिए, एक अमूर्त विधि घोषित किया गया था, इसे मूल निर्माता में बुलाएं, और बाल कक्षाओं में ओवरराइड करें। यह कामकाज किसी को भी हमारी समस्या का सामना करने में मदद कर सकता है ... सबसे अच्छा संबंध है! – alijunior

संबंधित मुद्दे

 संबंधित मुद्दे