2010-11-30 15 views
5

के साथ अद्वितीय पूर्णांक मान को जोड़ने की आवश्यकता है ठीक है, इसलिए मेरे पास एक बेस क्लास है जिसे हम TFruit पर कॉल करेंगे। इससे TApple, TOrange और इसी तरह के विभिन्न वंशज हैं। मुझे वंश की कक्षाओं के गुणों को एक फ़ाइल में सहेजने की ज़रूरत है।वर्ग

आदेश जब डेटा लोड हो रहा सही वर्ग बनाने के लिए सक्षम होने के लिए, हर वर्ग के एक ID है कि मैं वास्तविक डेटा लिखने से पहले फ़ाइल पर लिखने की जरूरत है। वर्तमान में, मैं यह कर के निम्नलिखित तरीके से ले कर आए हैं:

type 
    TFruit = class 
    const ID = 0; 
    end; 

    TApple = class(TFruit) 
    const ID = 1; 
    end; 

    TOrange = class(TFruit) 
    const ID = 2; 
    end; 

परीक्षण इस, मुझे पता चला कि मैं सुपर सावधान जो वर्ग मैं घोषणा होने की जरूरत है। अगर मैं इस का उपयोग करें:

var Fruit: TFruit; 

    Fruit := TOrange.Create; 

... तो Fruit.IDशून्य वापस आ जाएगी। हालांकि, घोषित करने Fruit एक TOrange के रूप में निकलेगा अपेक्षित परिणाम Fruit.ID = 2 (किसी को पता क्यों?)

तो मूल रूप से, मैं यह सही कर रहा हूँ या वहाँ यह करने के लिए एक बेहतर तरीका है? क्लास फ़ंक्शन बनाने और वहां से एक मूल्य वापस करने से तुलनात्मक रूप से बहुत बदसूरत लगता है (अतिरिक्त फ़ंक्शन घोषणा, कार्यान्वयन और कोड)।

उत्तर

5

समाधान बनाए रखने के लिए एक आसान मैपिंग क्लास बनाना होगा जहां आप उन सभी कक्षाओं को पंजीकृत करेंगे जिन्हें आप एक पूर्णांक में कनवर्ट करना चाहते हैं।

लाभ

  • क्षमता डुप्लिकेट पंजीकरण पता लगाने के लिए।
  • आपकी कक्षा संरचना का स्वतंत्र।
  • एक वर्गनाम में परिवर्तन को वापस शामिल करता है।

प्रयोग

RegisterClass.Register(0, TFruit); 
    RegisterClass.Register(1, TApple); 
    RegisterClass.Register(2, TOrange); 

कार्यान्वयन

TRegisterClass = class 
    private 
    FList: TStringList; 
    public 
    function FindID(AClass: TClass): Integer; 
    function FindClassName(const ID: Integer): string; 
    procedure Register(const ID: Integer; AClass: TClass); 
    end; 
    ... 
    function TRegisterClass.FindID(AClass: TClass): Integer; 
    begin 
    Assert(Assigned(AClass)); 

    Result := -1; 
    if FList.IndexOf(AClass.ClassName) <> -1 then 
     Result := Integer(FList.Objects[FList.IndexOf(AClass.ClassName)]); 
    end; 

    function TRegisterClass.FindClassName(const ID: Integer): string; 
    var 
    I: Integer; 
    begin 
    Result := EmptyStr; 
    for I := 0 to Pred(FList.Count) do 
     if Integer(FList.Objects[I]) = ID then 
     begin 
     Result := FList[I]; 
     Exit; 
     end; 
    end; 

    procedure TRegisterClass.Register(const ID: Integer; AClass: TClass); 
    begin 
    if IsAlreadyRegistered(ID) then 
     raise Exception.Create('Duplicate ID Registration') 
    else if IsAlreadyRegistered(AClass) then 
     raise Exception.Create('Duplicate Class Registration'); 

    FList.AddObject(AClass.ClassName, Pointer(ID)); 
    end; 

कृपया ध्यान दें बेहतर संरचनाओं एक पूर्णांक के लिए एक स्ट्रिंग मैप करने के लिए देखते हैं कि। इसे संकलक के बिना लिखना और डेल्फी 5 से परे कई बुनियादी संरचनाओं को नहीं जानना, मैंने एक स्पष्ट कार्यान्वयन चुना है।

नोट अभी भी है कि IsAlreadyRegistered अतिभारित कार्यों

+0

सुविधा के लिए कोई उस वर्ग की आईडी लौटाए गए पंजीकृत ऑब्जेक्ट्स के बेस-क्लास में क्लास विधि जोड़ सकता है। और चूंकि कक्षा के तरीके (सी # शैली स्थिर विधियों के विपरीत) वर्ग-प्रकार को 'स्वयं' पैरामीटर के रूप में प्राप्त करते हैं, यह अपेक्षा के अनुसार काम करता है। – CodesInChaos

+0

@CodeInChaos, सच है, लेकिन प्रत्येक कक्षा को इसकी पहचान करने के लिए "आईडी" की आवश्यकता होगी, जिसे इससे बचा जा सकता है और यह पता लगाने के लिए कि क्या अगली उपलब्ध आईडी है, यह जानने में नई वंशज पैदा हो रही है। –

+0

मेरा मतलब यह नहीं है कि यह विधि आपके 'FindID' फ़ंक्शन को' स्वयं 'के साथ सर्वो के रूप में कॉल करती है। लेकिन यह आर्किटेक्चररी रूप से बेहतर हो सकता है अगर वर्ग स्वयं आईडी के बारे में नहीं जानते हैं क्योंकि उन्हें धारावाहिक के कार्यान्वयन के विवरण के रूप में माना जा सकता है। – CodesInChaos

1

सबसे पहले, आप

type 
    TFruit = class 
    end; 

    TApple = class(TFruit) 
    end; 

    TOrange = class(TFruit) 
    end; 

आप की जरूरत है और फिर आप Fruit.ClassName और Fruit.ClassType उपयोग कर सकते हैं, नहीं कर सकते?

function ClassToID(const Fruit: TFruit): word; 
begin 
    if Fruit is TApple then 
    result := 1 
    else if Fruit is TOrange then 
    result := 2; 
end; 

या

TFruitClass = class of TFruit; 

type 
    TFruitAndID = record 
    FruitClass: TFruitClass; 
    ID: word; 
    end; 

const FruitIDs: array[0..1] of TFruitAndID = 
    ((FruitClass: TApple; ID: 1), (FruitClass: TOrange; ID: 2)); 

function ClassToID(Fruit: TFruit): word; 
var 
    i: Integer; 
begin 
    for i := 0 to high(FruitIDs) do 
    if FruitIDs[i].FruitClass = Fruit.ClassType then 
     Exit(FruitIDs[i].ID); 
end; 
+0

नहीं। मुझे फ़ाइल में कई सौ हजार "फलों" को बचाने की जरूरत है और अंतरिक्ष कारणों से मैं कक्षा के नाम का उपयोग नहीं करना चाहता हूं। मुझे एक संख्या की आवश्यकता है कि मैं फ़ाइल में एक शब्द (16-बिट) फ़ील्ड के रूप में लिखूंगा। – David

+0

@ डेविड: आप 'क्लासटॉइड' फ़ंक्शन लिख सकते हैं। –

1

आप डेल्फी 2010 का उपयोग कर रहे हैं तो आप attributes उपयोग कर सकते हैं आईडी के साथ अपनी कक्षाओं टैग करने के लिए।

+0

अफसोस की बात है कि मैं इस परियोजना के लिए डेल्फी 2005 का उपयोग कर रहा हूं। यदि यह डी 2010 था तो शायद मैं आपके सुझाव के साथ जाऊंगा। – David

3

लिखे जाने की वहाँ उदाहरण के लिए कई संभावनाएं हैं,:

function TFruit.GetClassId(): Word; 
begin 
    Result := CRC16(ClassName); 
end; 
+0

इससे अद्वितीय आईडी नहीं आती हैं। – CodesInChaos

+0

ऐसे छोटे तारों पर टकराव? असंभव के पास। –

2

किसी को पता क्यों?

क्योंकि आप कक्षा क्षेत्र घोषित कर रहे हैं? TFruit से विरासत विरासत, तो यह आईडी = 0 फ़ील्ड भी है। फिर आप इसे किसी अन्य आईडी = 2 फ़ील्ड के साथ ओवरराइड करते हैं। अब आपके पास इनमें से दो हैं। यदि आप ट्रागेट को ट्राफ्रेट में डाल देते हैं तो आपको विरासत क्षेत्र मिल रहा है, यह उन तक पहुंचने का एकमात्र तरीका है।

आप डेल्फी 2010+ पर हैं, तो उपयोग का श्रेय:

[ClassId(4)] TOrange = class(TFruit) 

लेकिन तुम क्यों पहली जगह में इन आईडी की जरूरत है? आपको अपने प्रत्येक वर्ग प्रकार को मैन्युअल रूप से चिह्नित करना होगा, यह त्रुटियों के लिए प्रवण है। बस वर्ग का नाम इस्तेमाल करें।

var t: TOrange; 
begin 
    writeFile(t.Classname, t.Data); 

आप अंतरिक्ष के साथ इतने चिंतित हैं, तो फ़ाइल की शुरुआत में एक classname-आईडी तालिका रखने के लिए और गतिशील आईडी निर्दिष्ट के रूप में तुम जाओ:

procedure WriteObject(c: TObject); 
var id: integer; 
begin 
    if not GetAlreadyRegisteredClassnameId(c.Classname, id) then 
    id := AddClassnameToTable(c.Classname); 

    writeToCache(id, c.Data) 
end; 

procedure WriteFile() 
var i: integer; 
begin 
    for i := 0 to ObjectCount-1 do 
    WriteObject(objects[i]); 
    OutputClassnameTableToFile; 
    OutputObjectCacheToFile; 
end; 

(बेशक अनदेखी स्मृति की कमी यहां प्रदर्शन उद्देश्यों के लिए, लेकिन मेमोरी कैश के बिना भी ऐसा करना आसान है)

0

अन्य कोण पर देख रहे हैं: क्यों आईडी केवल पढ़ने योग्य ऑब्जेक्ट प्रॉपर्टी नहीं है (क्लास कॉन्स के बजाय)?

तो:

type 
    TFruit = class 
    protected 
    FId: Integer; 
    published 
    property ID:Integer read FId; 
    end; 

    TApple = class(TFruit) 
    constructor Create; 
    end; 

    TOrange = class(TFruit) 
    constructor Create; 
    end; 

<...> 
constructor TApple.Create; 
begin 
    FId := 1; 
end; 

constructor TOrange.Create; 
begin 
    FId := 2; 
end; 

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

+0

आरटीटीआई काम करने के लिए, आईआईआरसी संपत्ति को 'प्रकाशित' दृश्यता (प्रश्न में उदाहरण कोड स्पष्ट रूप से प्रकाशित उपयोग की आवश्यकता है) – mjn

+0

@mjustin - यह डेल्फी संस्करण 2010+ पर निर्भर करता है आरटीटीआई –

+0

@Gerry: कोड आवश्यक है डेल्फी 2005 के साथ काम करने के लिए (जैसा कि मैंने एक टिप्पणी में देखा है), लेकिन आप सही हैं :) – mjn

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