2012-02-06 12 views
11

मैं किसी ऑब्जेक्ट की संपत्ति के रूप में रिकॉर्ड करना चाहता हूं। समस्या यह है कि जब मैं इस रिकॉर्ड के किसी एक फ़ील्ड को बदलता हूं, तो वस्तु को परिवर्तन के बारे में पता नहीं है।क्या किसी ऑब्जेक्ट की संपत्ति के रूप में रिकॉर्ड रिकॉर्ड किया जा सकता है?

type 
    TMyRecord = record 
    SomeField: Integer; 
    end; 

    TMyObject = class(TObject) 
    private 
    FSomeRecord: TMyRecord; 
    procedure SetSomeRecord(const Value: TMyRecord); 
    public 
    property SomeRecord: TMyRecord read FSomeRecord write SetSomeRecord; 
    end; 

और फिर अगर मैं करता हूँ ...

MyObject.SomeRecord.SomeField:= 5; 

... काम नहीं करेगा।

तो जब मैं रिकॉर्ड के फ़ील्ड में से एक लिखा जाता हूं तो मैं संपत्ति सेटिंग प्रक्रिया 'पकड़' कैसे बना सकता हूं? रिकॉर्ड रिकॉर्ड करने के तरीके में शायद कुछ चाल है?

अधिक जानकारी

मेरा लक्ष्य एक TObject या एक OnChange घटना के साथ TPersistent (जैसे TFont या TStringList के रूप में) बनाने के लिए होने से बचने के लिए है। मैं इसके लिए ऑब्जेक्ट्स का उपयोग करने से परिचित हूं, लेकिन क्लीनअप मेरे कोड को थोड़ा सा करने के प्रयास में, मैं देख रहा हूं कि मैं इसके बजाय रिकॉर्ड का उपयोग कर सकता हूं। मुझे बस यह सुनिश्चित करने की ज़रूरत है कि जब मैं रिकॉर्ड के फ़ील्ड में से एक सेट करता हूं तो मेरे रिकॉर्ड प्रॉपर्टी सेटर को ठीक से कॉल किया जा सकता है।

+0

क्या यह एक 'पैक रिकॉर्ड' संभवतः संभवतः काम में आता है? –

+1

'पैक' या अन्यथा बिंदु के बगल में है। यह सिर्फ रिकॉर्ड के लेआउट और संरेखण को नियंत्रित करता है। मौलिक मुद्दा मूल्य प्रकारों और संदर्भ प्रकारों के बीच अंतर है। वास्तव में, आपके प्रश्न में कोड संकलित भी नहीं करता है। –

+0

@ डेविड हेफरनन बहुत सच है, मैंने वास्तव में उस कोड को डेल्फी में नहीं रखा है, इसे सीधे यहां टाइप किया है। –

उत्तर

11

इस लाइन पर विचार करें कि आप रिकॉर्ड प्रकार के मूल्य की प्रतिलिपि बनाते हैं ओकेसी चर MyRecord। फिर आप इस स्थानीय प्रतिलिपि के एक क्षेत्र को संशोधित करते हैं। यह MyObject में आयोजित रिकॉर्ड को संशोधित नहीं करता है। ऐसा करने के लिए आपको संपत्ति सेटटर का आह्वान करने की आवश्यकता है।

MyRecord := MyObject.SomeRecord; 
MyRecord.SomeField := NewValue; 
MyObject.SomeRecord := MyRecord; 

या किसी संदर्भ प्रकार का उपयोग करने के लिए स्विच करें, यानी रिकॉर्ड के बजाए एक वर्ग।

संक्षेप में, आपके वर्तमान कोड के साथ समस्या यह है कि SetSomeRecord नहीं कहा जाता है और इसके बजाय आप रिकॉर्ड की एक प्रति संशोधित कर रहे हैं। और ऐसा इसलिए है क्योंकि एक रिकॉर्ड मूल्य प्रकारसंदर्भ प्रकार होने के विपरीत है।

+0

+1 तो इसका मतलब है कि रिकॉर्ड संपत्ति सेटटर को अपने क्षेत्रों में संशोधन करने का कोई तरीका नहीं है? बमर :( –

+0

यह सही है। –

+1

यह ऑब्जेक्ट-आधारित गुणों के लिए भी सच है, यही कारण है कि 'टीएफओटी',' टीएसट्रिंग्स 'जैसी कक्षाएं' ऑन चेंज 'घटना होती हैं - मूल घटक उस पर एक आंतरिक ईवेंट हैंडलर निर्दिष्ट करता है घटना इसलिए उप-गुणों में परिवर्तन को मूल घटक तक बढ़ाया जा सकता है, क्योंकि इसकी स्थिति सेटर को उस स्थिति में नहीं बुलाया जाएगा। –

1

आप मूल्य से रिकॉर्ड पास कर रहे हैं, इसलिए रिकॉर्ड की एक प्रति वस्तु द्वारा संग्रहित की जाती है। उस बिंदु से प्रभावी रूप से दो वस्तुएं हैं; ऑब्जेक्ट द्वारा आयोजित मूल और प्रतिलिपि। एक बदलना दूसरे को नहीं बदलेगा।

आपको संदर्भ द्वारा रिकॉर्ड पास करने की आवश्यकता है।

type 
    TMyRecord = record 
    SomeField: Integer; 
    end; 
    PMyRecord = ^TMyRecord; 

    TMyObject = class(TObject) 
    private 
    FSomeRecord: PMyRecord; 
    procedure SetSomeRecord(const Value: PMyRecord); 
    public 
    property SomeRecord: PMyRecord read FSomeRecord write SetSomeRecord; 
    end; 
+6

यह कक्षा का उपयोग करने के लिए बहुत अजीब बनाता है क्योंकि आप कक्षा के ग्राहकों को रिकॉर्ड आवंटित करने के लिए मजबूर करते हैं और सुनिश्चित करते हैं कि इसका जीवनकाल उस वस्तु को ट्रैक करता है। निश्चित रूप से आप कक्षा में रिकॉर्ड स्टोर करना चाहते हैं और केवल पढ़ने-योग्य सूचक संपत्ति का पर्दाफाश करना चाहते हैं जो गेटटर फ़ंक्शन के साथ लागू किया गया है। परिणाम के रूप में कार्यान्वित: = @FSomeRecord जहां FSomeRecord TMyRecord है। –

+0

और यदि आप ऐसा करने जा रहे हैं तो यह वर्ग के सार्वजनिक क्षेत्र के रूप में रिकॉर्ड घोषित करने के बराबर है और आप ऐसा भी कर सकते हैं क्योंकि यह समझना आसान बनाता है। –

+0

@ डेविड हेफरन सहमत हैं, जो कि और अधिक कठिनाइयों का परिचय देता है। –

4

रिकॉर्ड के बजाए टॉब्जेक्ट का उपयोग करने के बारे में कैसे?/To/मूल्यों को निर्धारित रिकॉर्ड से

MyObject.MyProperties.SomeField := 1; 
x := MyObject.MyProperties.SomeField; 

इस पद्धति का उपयोग करके आप व्यक्तिगत रूप से प्राप्त करने की आवश्यकता नहीं है:

type 
    TMyProperties = class(TObject) 
    SomeField: Integer; 
    end; 

    TMyObject = class(TObject) 
    private 
    FMyProperties: TMyProperties;  
    public 
    constructor Create; 
    destructor Destroy; override; 

    property MyProperties: TMyRecord read FMyProperties; 
    end; 

implementation 

constructor TMyObject.Create; 
begin 
    FMyProperties := TMyProperties.Create; 
end; 

destructor TMyObject.Destroy; 
begin 
    FMyProperties.Free; 
end; 

अब आप इस तरह पढ़ सकते हैं और TMyProperties के गुणों की स्थापना की। यदि आपको FMyProperties में परिवर्तनों को पकड़ने की आवश्यकता है, तो आप संपत्ति घोषणा में 'सेट' प्रक्रिया जोड़ सकते हैं।

MyObject.SomeRecord.SomeField := NewValue; 

यह वास्तव में एक संकलन त्रुटि है:

MyRecord := MyObject.SomeRecord; 
MyRecord.SomeField := NewValue; 

यहाँ क्या होता है:

[DCC Error]: E2064 Left side cannot be assigned to

आपका वास्तविक कोड शायद कुछ इस तरह है

+0

वास्तव में, मैं अक्सर इसके लिए ऑब्जेक्ट्स (और लगातार) का उपयोग करता हूं। इसमें आमतौर पर कुछ ऐसा होता है जो आपके पास नहीं है: ऑन चेंज इवेंट। अनिवार्य रूप से, मेरा पूरा बिंदु 'SetSomeRecord' प्रक्रिया में इस रिकॉर्ड (या ऑब्जेक्ट) के परिवर्तनों को पकड़ने में सक्षम होना है।मैं यह देखने के लिए देख रहा था कि क्या मैं 'ऑन चेंज' ईवेंट की आवश्यकता के बिना रिकॉर्ड में इसे एक साथ जोड़ सकता हूं। –

10

आखिरकार आप रिकॉर्ड के फ़ील्ड तक पहुंचना चाहेंगे, फिर भी जब आप प्रस्ताव देते हैं, तो रिकॉर्ड अक्सर कक्षा के भीतर एक उपयुक्त अमूर्त विकल्प होता है।

type 
    TMyRec = record 
    SomeRecInteger: integer; 
    SomeRecString: string; 
    end; 

    TMyClass = class(TObject) 
    private 
    FMyRec: TMyRec; 
    procedure SetSomeString(const AString: string); 
    public 
    property SomeInteger: integer read FMyRec.SomeRecInteger write FMyRec.SomeRecInteger; 
    property SomeString: string read FMyRec.SomeRecString write SetSomeString; 
    end; 

procedure TMyClass.SetSomeRecString(const AString: string); 
begin 
    If AString <> SomeString then 
    begin 
    // do something special if SomeRecString property is set 
    FMyRec.SomeRecString := AString; 
    end; 
end; 

नोट::

  1. रिकॉर्ड संपत्ति SomeRecInteger
  2. SetSomeRecString के उपयोग के लिए सीधी पहुँच कुछ विशेष प्रसंस्करण इस पर लागू करने के लिए इस प्रकार एक वर्ग बड़े करीने से एक रिकार्ड के गुणों का उपयोग कर सकते केवल क्षेत्र

उम्मीद है कि इससे मदद मिलती है।

+0

+1 सुविधा के लिए मैं एक ऐसी प्रक्रिया भी जोड़ूंगा जो एक TMyRec पैरामीटर लेता है ताकि मैं कर सकूं सभी मानों को एक ही समय में सेट करें। –

+1

+1 जो कुछ मैं ढूंढ रहा था वह काफी नहीं था, लेकिन मुझे कुछ नया सिखाया: मैं एन कभी पता था कि आप संपत्ति के गेटर्स/सेटर्स के रूप में रिकॉर्ड के खेतों का उपयोग कर सकते हैं। –

4

रिकॉर्ड का सेटटर/गेटर हिस्सा क्यों नहीं बनाते?

TMyRecord = record 
    fFirstname: string; 
    procedure SetFirstName(AValue: String); 
property 
    Firstname : string read fFirstname write SetFirstName; 
end; 

TMyClass = class(TObject) 
    MyRecord : TMyRecord; 
end; 

procedure TMyRecord.SetFirstName(AValue: String); 
begin 
    // do extra checking here 
    fFirstname := AValue; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    MyClass: TMyClass; 
begin 
    MyClass := TMyClass.Create; 
    try 
    MyClass.MyRecord.Firstname := 'John'; 
    showmessage(MyClass.MyRecord.Firstname); 
    finally 
    MyClass.Free; 
    end; 
end; 
+0

वाह, प्रतीक्षा करें, आप यह कर सकते हैं ?! यह हर ज्ञान को मोड़ता है जो मेरे पास कभी रिकॉर्ड था! –

+0

हाँ, इसे नए कंपाइलर्स में जोड़ा गया था, यह सुनिश्चित नहीं है कि यह किस संस्करण से शुरू हुआ (मैं 2010 का उपयोग कर रहा हूं) .. –

+1

यहां संपत्ति सेटर्स बेकार हैं। आपने वास्तव में जो किया वह एक संपत्ति के बजाय सार्वजनिक सदस्य के रूप में रिकॉर्ड का पर्दाफाश कर रहा था। यह बड़ा बदलाव है। –

0

यह @ SourceMaid के उत्तर का एक विकल्प है।

आप अपने ऑब्जेक्ट के अंदर एक रिकॉर्ड (रिकॉर्ड के लिए पॉइंटर नहीं) का उपयोग कर सकते हैं और केवल एक ही संपत्ति पढ़ सकते हैं जो आपके रिकॉर्ड में पॉइंटर लौटाती है।

वर्ग:

type 
    TMyRecord = record 
    I:Integer; 
    end; 
    PMyRecord = ^TMyRecord; 
    TMyObject = class 
    private 
    FMyRecord:TMyRecord; 
    function GetMyRecordPointer: PMyRecord; 
    public 
    property MyRecord: PMyRecord read GetMyRecordPointer; 
    end; 

गेटर:

function TMyObject.GetMyRecordPointer: PMyRecord; 
begin 
    result := @FMyRecord; 
end; 

उपयोग:

o := TMyObject.Create; 
o.MyRecord.I := 42; 
ShowMessage(o.MyRecord.I.ToString); 
o.MyRecord.I := 23; 
ShowMessage(o.MyRecord.I.ToString); 
o.Free; 

आप एक सेटर की जरूरत है, क्योंकि आप एक संदर्भ मिलता है और के साथ काम नहीं है। इसका मतलब है कि आप एक नया सौंपकर पूरे रिकॉर्ड को नहीं बदल सकते हैं।
लेकिन आप त्रुटि "Left side cannot be assigned to" त्रुटि के बिना सीधे रिकॉर्ड के तत्वों का उपयोग कर सकते हैं।

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