2014-10-02 11 views
7

के रिकॉर्ड के लिए टूट गया है, तो यह समस्या reproduces:TDictionary हैशिंग तार

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    Generics.Collections; 
type 
    TStringRec = record 
    s1 : string; 
    s2 : string; 
    end; 
    TGetHash<TKey,TValue> = class(TEnumerable<TPair<TKey,TValue>>) 
    public 
    type 
     TItem = record 
     HashCode: Integer; 
     Key: TKey; 
     Value: TValue; 
     end; 
     TItemArray = array of TItem; 
    public 
    FItems: TItemArray; 
    end; 
var 
    LCrossRef : TDictionary<TStringRec, integer>; 
    LRec : TStringRec; 
    i : integer; 
begin 
    LCrossRef := TDictionary<TStringRec, integer>.Create(); 
    LRec.s1 := 'test1'; 
    LRec.s2 := 'test2'; 
    LCrossRef.Add(LRec, 1); 
    LRec.s1 := 'test1'; 
    LRec.s2 := 'test2'; 
    if LCrossRef.TryGetValue(LRec, i) then begin 
    writeln('ok'); 
    end else begin 
    LCrossRef.Add(LRec, 1); 
    for i := Low(TGetHash<TStringRec, integer> 
       (LCrossRef).FItems) 
      to High(TGetHash<TStringRec, integer> 
       (LCrossRef).FItems) do 
     WriteLn(TGetHash<TStringRec, integer>(LCrossRef).FItems[i].HashCode); 
    WriteLn('not ok'); 
    end; 
    ReadLn; 
end. 

शब्दकोश आइटम को पुनः प्राप्त करने में विफल रहता है और समान तार युक्त रिकॉर्ड के लिए एक अलग HashCode उत्पन्न करता है।

यह आंशिक रूप से QC-#122791 में उल्लेख किया है, लेकिन वैकल्पिक हल पैक रिकॉर्ड का उपयोग करने के लिए तार के रिकॉर्ड के लिए काम नहीं करता है (कम से कम ऊपर के उदाहरण भी जब TStringRecpacked record के रूप में घोषित किया जाता है विफल रहता है)।

क्या इसके लिए कोई समझदार कामकाज है?

मेरी वर्तमान रणनीति उन तारों को जोड़ना है जो अन्यथा रिकॉर्ड में शामिल हो जाती हैं और इसके बजाय TDictionary<string, TValue> का उपयोग करती हैं, लेकिन यह स्वाभाविक रूप से असंतुष्ट है।

+0

एक कस्टम IEqualityComparer के साथ TObjectDictionary का उपयोग करने के बारे में कैसे है जो आपके विशिष्ट प्रकार के लिए GetHashCode लागू करता है? –

+0

@VilleKrumlinde आप 'TObjectDictionary' का मतलब नहीं है। यहां कोई ऑब्जेक्ट स्वामित्व नहीं है। –

+0

'IEqualityComparer' को लागू करना निश्चित रूप से जाने का एक तरीका है। –

उत्तर

6

यह एक ज्ञात सीमा है, जो डिज़ाइन द्वारा है। रिकॉर्ड के लिए डिफ़ॉल्ट तुलनाकर्ता और हैशर केवल शुद्ध मूल्य प्रकार के रिकॉर्ड के लिए काम करते हैं, और ऐसे रिकॉर्ड के लिए जिनके पास कोई पैडिंग नहीं है।

डिज़ाइनर/हैश रिकॉर्ड की तुलना करने के लिए आरटीटीआई का उपयोग करने का विकल्प चुन सकते थे। हालांकि, उन्होंने ऐसा करने का विकल्प नहीं चुना। उस विकल्प के लिए कुछ स्पष्ट व्यावहारिक कारण हैं:

  1. वे अनिच्छुक पर आरटीटीआई के उपयोग को मजबूर नहीं करना चाहते थे।
  2. आरटीटीआई के उपयोग से होने वाली एक महत्वपूर्ण प्रदर्शन हिट है।

जेनेरिक संग्रह का उपयोग करते समय इसके साथ निपटने का तरीका अपने स्वयं के तुलनाकर्ता और हैशर्स की आपूर्ति करना है।

स्ट्रिंग्स को जोड़ने की आपकी वर्तमान रणनीति काम नहीं करेगी। 'a' और 'aa' पर विचार करें, और फिर 'aa' और 'a' पर विचार करें। टेक्स्ट आधारित दृष्टिकोण का उपयोग करने के लिए आप जेएसओएन को रिकॉर्ड को क्रमबद्ध करना चाहते हैं।

+0

मैं सहमत हूं कि यह सामान्य रूप से संदर्भ प्रकारों के लिए समझ में आता है।विशेष और कंपाइलर प्रबंधित होने के नाते, तारों के रूप में, हालांकि, मुझे उम्मीद थी कि कम से कम वे एक समझदार तरीके से डिफ़ॉल्ट रूप से धोए जाएंगे (यह असाधारण या असंभव प्रतीत नहीं होता है)। कस्टम तुलनाकर्ता और हैशर्स यह है। सहमत हैं कि concatenation एक बकवास रणनीति है - मेरे मामले में यह एक हैक था कि, विशिष्ट समस्या की प्रकृति से, वस्तुतः आपके द्वारा नोट किए गए तरीके से संघर्ष करने की गारंटी नहीं है। यह एक अस्थायी कामकाज था जब तक कि मैं एक बेहतर रणनीति पर बस गया। –

+0

इसे कैसे लागू किया जाएगा? सिस्टम को आरटीटीआई आधारित तुलनाकर्ता उत्पन्न करने में सक्षम होना होगा। मैं तुच्छ नहीं हूं। –

+0

शायद। मैंने स्वीकार किया है कि इसे किसी भी कठोरता के साथ नहीं सोचा है। किसी भी मामले में, बिंदु मंथन है। इसे एक और समाधान होना चाहिए। –

2

मेरे कोडबेस से उदाहरण का उपयोग करके डेविड के उत्तर पर विस्तार करने के लिए। मैं एक शब्दकोश

Records: TDictionary<TGazetteerRecord,TGazetteerRecord> 

जो

Records := TDictionary<TGazetteerRecord,TGazetteerRecord>.Create(InitCapacity, TGazRecordComparer.Create); 

क्या इस काम शब्दकोश के निर्माण में एक कस्टम comparer रहा है बनाता है instantiated है।

TGazRecordComparer = class(TEqualityComparer<TGazetteerRecord>) 
private 
public 
    function Equals(const Left, Right: TGazetteerRecord): Boolean; override; 
    function GetHashCode(const Value: TGazetteerRecord): Integer; override; 
end; 

इसके लिए कार्यान्वयन डिफ़ॉल्ट प्रकार को रिकॉर्ड प्रकार के लिए बदल देता है। मेरा उदाहरण वास्तव में रिकॉर्ड के बजाए कक्षा का उपयोग करता है लेकिन मुझे नहीं लगता कि यह रिकॉर्ड प्रकार के साथ पूरी तरह से ठीक क्यों नहीं काम करना चाहिए। ध्यान दें कि तुलनाकर्ता वर्ग संदर्भित है और इसलिए शब्दकोश को नष्ट होने पर स्वचालित रूप से निपटाया जाएगा।

+1

मुझे इस बिंदु को नहीं दिख रहा है। कार्यान्वयन कहां है। आप हमें बता रहे हैं कि आपने एक तुलनात्मक लागू किया है।एफडब्ल्यूआईडब्ल्यू ऐसा करने का सामान्य तरीका है कन्स्ट्रक्ट क्लास विधि को कॉल करना। –

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