2009-03-06 17 views
16
में रिकॉर्ड प्रकार की संपत्तियों के लिए "बाईं ओर करने के लिए आवंटित नहीं किया जा सकता"

मुझे पता है क्यों डेल्फी रिकॉर्ड प्रकार गुण व्यवहार करता है उत्सुक हूँ के रूप में केवल पढ़ने के लिए:डेल्फी

TRec = record 
    A : integer; 
    B : string; 
    end; 

    TForm1 = class(TForm) 
    private 
    FRec : TRec; 
    public 
    procedure DoSomething(ARec: TRec); 
    property Rec : TRec read FRec write FRec; 
    end; 

अगर मैं में से किसी को कोई मान निर्दिष्ट करने की कोशिश Rec संपत्ति के सदस्यों, मैं मिल जाएगा त्रुटि "बाईं ओर करने के लिए आवंटित नहीं किया जा सकता":

procedure TForm1.DoSomething(ARec: TRec); 
begin 
    Rec.A := ARec.A; 
end; 

जबकि अंतर्निहित क्षेत्र के साथ एक ही कर सकता है:

procedure TForm1.DoSomething(ARec: TRec); 
begin 
    FRec.A := ARec.A; 
end; 

क्या उस व्यवहार के लिए कोई स्पष्टीकरण है?

सादर

उत्तर

27

के बाद से "रेक" एक संपत्ति, संकलक व्यवहार करता है थोड़ा अलग है क्योंकि यह पहले मूल्यांकन करने के लिए संपत्ति डीईसीएल की "पढ़ें" है। इस पर विचार करें, जो शब्दार्थ अपने उदाहरण के बराबर है:

... 
property Rec: TRec read GetRec write FRec; 
... 

तो आप इसे इस को देखें, तो आप देख सकते हैं कि पहला संदर्भ, "रेक" (पहले डॉट '।'), GetRec कॉल करने के लिए है , जो रिक की अस्थायी स्थानीय प्रतिलिपि बनाएगा। ये अस्थायी डिजाइन "केवल पढ़ने के लिए" हैं। यह वही है जो आप चल रहे हैं।

एक और बात आप यहाँ कर सकते युक्त वर्ग पर गुण के रूप में रिकॉर्ड के अलग-अलग क्षेत्रों से बाहर तोड़ने के लिए है:

... 
property RecField: Integer read FRec.A write FRec.A; 
... 

इससे आप सीधे एम्बेडेड है कि के क्षेत्र के लिए संपत्ति के माध्यम से आवंटित करने के लिए अनुमति देगा कक्षा के उदाहरण में रिकॉर्ड।

+1

+1 आपका जवाब के बाद यह 4 साल से टकरा की तरह कुछ करना होगा! –

4

क्योंकि आपके पास अंतर्निहित गेटर और सेटर फ़ंक्शन हैं और आप फ़ंक्शन के परिणाम को संशोधित नहीं कर सकते क्योंकि यह एक कॉन्स्ट पैरामीटर है।

(नोट: यदि आप किसी ऑब्जेक्ट में रिकॉर्ड को बदलते हैं, तो परिणाम वास्तव में एक पॉइंटर होगा, इस प्रकार एक var पैरामीटर के बराबर होगा)।

यदि आप एक रिकॉर्ड के साथ रहना चाहते हैं, तो आपको इंटरमीडिएट वैरिएबल (या फ़ील्ड वेरिएबल) का उपयोग करना होगा या एक कथन का उपयोग करना होगा।

स्पष्ट गेटर और सेटर कार्यों के साथ निम्नलिखित कोड में भिन्न व्यवहार देखें:

type 
    TRec = record 
    A: Integer; 
    B: string; 
    end; 

    TForm2 = class(TForm) 
    private 
    FRec : TRec; 
    FRec2: TRec; 
    procedure SetRec2(const Value: TRec); 
    function GetRec2: TRec; 
    public 
    procedure DoSomething(ARec: TRec); 
    property Rec: TRec read FRec write FRec; 
    property Rec2: TRec read GetRec2 write SetRec2; 
    end; 

var 
    Form2: TForm2; 

implementation 

{$R *.dfm} 

{ TForm2 } 

procedure TForm2.DoSomething(ARec: TRec); 
var 
    LocalRec: TRec; 
begin 
    // copy in a local variable 
    LocalRec := Rec2; 
    LocalRec.A := Arec.A; // works 

    // try to modify the Result of a function (a const) => NOT ALLOWED 
    Rec2.A := Arec.A; // compiler refused! 

    with Rec do 
    A := ARec.A; // works with original property and with! 
end; 

function TForm2.GetRec2: TRec; 
begin 
    Result:=FRec2; 
end; 

procedure TForm2.SetRec2(const Value: TRec); 
begin 
    FRec2 := Value; 
end; 
19

हाँ यह एक समस्या है। लेकिन रिकॉर्ड गुणों का उपयोग करके हल किया जा सकता है:

type 
    TRec = record 
    private 
    FA : integer; 
    FB : string; 
    procedure SetA(const Value: Integer); 
    procedure SetB(const Value: string); 
    public 
    property A: Integer read FA write SetA; 
    property B: string read FB write SetB; 
    end; 

procedure TRec.SetA(const Value: Integer); 
begin 
    FA := Value; 
end; 

procedure TRec.SetB(const Value: string); 
begin 
    FB := Value; 
end; 

TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
private 
    { Private declarations } 
    FRec : TRec; 
public 
    { Public declarations } 
    property Rec : TRec read FRec write FRec; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    Rec.A := 21; 
    Rec.B := 'Hi'; 
end; 

यह बिना किसी समस्या के संकलित और काम करता है।

+3

+1 नोट करें कि आपका समाधान खराब नहीं है, लेकिन इसके उपयोगकर्ताओं को यह याद रखना होगा कि यदि उन्होंने कभी भी संपत्ति को "संपत्ति रिक: TREC पढ़ा है GetRec लिखें FRec;", असाइनमेंट चाल खराब तरीके से विफल हो जाएगी (क्योंकि GetRec एक * प्रतिलिपि * रिकॉर्ड के रूप में * मूल्य प्रकार *) हैं। –

+0

TForm1 में रिक प्रॉपर्टी केवल तभी पढ़ी जा सकती है जब रिकॉर्ड के गुणों को केवल पढ़ने/लिखने की आवश्यकता हो। इस समाधान के लिए मुख्य भाग रिकॉर्ड के गुणों में सेटर विधियां हैं। – Griffyn

8

कंपाइलर आपको अस्थायी रूप से असाइन करने से रोक रहा है। सी # के बराबर अनुमत है, लेकिन इसका कोई प्रभाव नहीं है; रिक प्रॉपर्टी का रिटर्न वैल्यू अंतर्निहित क्षेत्र की एक प्रति है, और कॉपी पर फ़ील्ड को असाइन करना एक एनओपी है।

2

अन्य लोगों की तरह कहा है - पढ़ी गई संपत्ति रिकॉर्ड की प्रतिलिपि वापस कर देगी, इसलिए फ़ील्ड का असाइनमेंट TForm1 के स्वामित्व वाली प्रतिलिपि पर कार्य नहीं कर रहा है।

एक अन्य विकल्प है कुछ की तरह:

TRec = record 
    A : integer; 
    B : string; 
    end; 
    PRec = ^TRec; 

    TForm1 = class(TForm) 
    private 
    FRec : PRec; 
    public 
    constructor Create; 
    destructor Destroy; override; 

    procedure DoSomething(ARec: TRec); 
    property Rec : PRec read FRec; 
    end; 

constructor TForm1.Create; 
begin 
    inherited; 
    FRec := AllocMem(sizeof(TRec)); 
end; 

destructor TForm1.Destroy; 
begin 
    FreeMem(FRec); 

    inherited; 
end; 

डेल्फी होगा आप के लिए PREC सूचक है, इसलिए इस तरह की बातें अभी भी काम करेगा भिन्नता:

Form1.Rec.A := 1234; 

के लिखने भाग के लिए कोई ज़रूरत नहीं है संपत्ति, जब तक आप पीआरईसी बफर को स्वैप करना नहीं चाहते हैं कि FREC अंक पर। मैं वास्तव में किसी संपत्ति के माध्यम से इस तरह के स्वैपिंग करने का सुझाव नहीं दूंगा।

2

सबसे सरल तरीका है:

procedure TForm1.DoSomething(ARec: TRec); 
begin 
    with Rec do 
    A := ARec.A; 
end; 
+0

मुझे लगता है कि आप सही हैं - रिकॉर्ड के लिए गुणों का उपयोग करने का कोई मतलब नहीं है, यह बहुत काम की तरह लगता है ... बस एक प्रक्रिया है जो रिकॉर्ड के लिए कुछ करता है: SetSomething (var ARec: TRec) – sergeantKK

3

इसका कारण यह है संपत्ति वास्तव में एक समारोह के रूप में पालन किया जाता है। गुण केवल एक मूल्य वापस या सेट करें।

Testing.getTestRecord().I := 10; //error (i think) 

आप क्या कर सकते है:

r := Testing.TestRecord; // read 
r.I := 10; 
Testing.TestRecord := r; //write 

Testing.TestRecord.I := 10; // error 

इस तरह एक समारोह कॉल करने जैसा ही है: यह एक संदर्भ या रिकॉर्ड

इतना करने के लिए एक सूचक नहीं है

यह थोड़ा गन्दा है लेकिन इस प्रकार के वास्तुकला में अंतर्निहित है।

7

एक समाधान जिसका मैं अक्सर उपयोग करता हूं वह संपत्ति को रिकॉर्ड के लिए सूचक के रूप में घोषित करना है।

type 
    PRec = ^TRec; 
    TRec = record 
    A : integer; 
    B : string; 
    end; 

    TForm1 = class(TForm) 
    private 
    FRec : TRec; 

    function GetRec: PRec; 
    procedure SetRec(Value: PRec); 
    public 
    property Rec : PRec read GetRec write SetRec; 
    end; 

implementation 

function TForm1.GetRec: PRec; 
begin 
    Result := @FRec; 
end; 

procedure TForm1.SetRec(Value: PRec); 
begin 
    FRec := Value^; 
end; 
इस के साथ

, सीधे बताए Form1.Rec.A := MyInteger काम करेंगे, लेकिन यह भी Form1.Rec := MyRec अपेक्षा के अनुरूप FRec क्षेत्र के लिए MyRec में सभी मूल्यों को कॉपी करके काम करेंगे।

केवल यहाँ ख़तरा है कि जब आप वास्तव में रिकॉर्ड के साथ काम करने के लिए की प्रतिलिपि प्राप्त करना चाहते हैं, तो आप MyRec := Form1.Rec^

+0

शानदार और पेशेवर – Vassilis