الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

كيف أجعل ال Index للمصفوفة نص ؟

بدأه أبو محمد اللحياني في 30 سبتمبر 2009 · 13 رد · 1,326 مشاهدة · في لغة Delphi
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورحمة الله وبركاته

لقد بحثت هنا وفي الانترنت ولم أجد طريقة مختصرة

كل التعليمات التي وجدتها تفرض أن يكون الـ key رقم integer .

وأقول هذا لأجل الوصول السريعة للقيم بدون معرفة رقم الترتيبي لها .

جربت وضع نوع سجل به key, value ثم وجدت الرقم يلاحقني في المصفوفة

type
  TConfigRec = Record
	 key : string[50];
	 value  : string[50];
	 //input : string;
   end;
   TConfigArr = array of TConfigRec;
var
  Config : TConfigArr;

procedure loadCfg( var aCfg : TConfigArr);
var
  i, n, index: Integer;
  objIni : TIniFile;
  iniFileName : String;
  Sections, KeyList :TStringList;
begin
  iniFileName := GetCurrentDir() + '\cfg.ini';
  if FileExists(iniFileName) Then begin
	try
	  Sections :=TStringList.Create;
	  KeyList := TStringList.Create;
	  objIni := TIniFile.Create(iniFileName);
	  objIni.ReadSections(Sections);
	  for i := 0  to Sections.Count - 1 do begin
		objIni.ReadSection(Sections.ValueFromIndex, KeyList);
		index := Length(aCfg);
		  for n := 0 to KeyList.Count - 1 do begin
			SetLength(aCfg, Length(aCfg) + 1);
			inc(index);
			aCfg[index].key := KeyList[n];
			aCfg[index].value:= objIni.ReadString(Sections.ValueFromIndex, KeyList[n], '');

		  end;
	  end;
	finally
	  Sections.Free;
	  KeyList.Free;
	  objIni.Free;
	end; // try
  end; // if
end;

المطلوب لا أريد

aCfg[index].key := '';
aCfg[index].value:= '';

أريد :

aCfg[KeyList[n]] = objIni.ReadString(Sections.ValueFromIndex, KeyList[n], '');;

تم تعديل هذه المشاركة بواسطة أبو محمد اللحياني في 30 سبتمبر 2009 في 17:51

ولو وافيت ربك دون ذنب *** وناقشك الحساب إذاً هلكتا

ولم يظلمك في عملٍ ولكن *** عسير أن تقوم بما حملتا

#2

السلام عليكــم ورحمـة الله وبركاتــه ،،

لأ أعتقد أنه يمكن إستخدام نص كمؤشر للمصفوفة لأنه ليس من نمط ترتيبي فقط الأنماط الترتيبية مسموحة

var
 i: Char;
 arr: array ['a'..'d']of Char;
 begin
   for i:= 'a' to 'd' do
	begin
	 arr := i;
	 ShowMessage(Arr);
	end;
 end;

كالنمط Char ولكن إن توصلت لحل أرجو وضعه هنا لنستفيد منه

أتمنى التوفيق

تم تعديل هذه المشاركة بواسطة حسين طي في 30 سبتمبر 2009 في 22:40

#3

السلام عليكم...

يمكن استعمال بعض الحيل، ليس كمصفوفة تماماً، و لكن لإنجاز ما فهمته من كود الأخ "أبو محمد اللحياني".

في بداية القسم interface نقوم بتعريف الأنواع التالية:

  1.  
  2. TValueHolderArray = array of string;
  3.  
  4. TValueHolderMap = record
  5. AName: string;
  6. AValue: string;
  7. end;
  8.  
  9. TValueHolder = class(TObject)
  10. private
  11. FValues: array of TValueHolderMap;
  12.  
  13. procedure SetValue(const AName, AValue: string);
  14. function GetValue(const AName: string): string;
  15. public
  16. destructor Destroy;
  17. procedure Reset;
  18. function ValuesAsNormalArray: TValueHolderArray;
  19. property Value[const Index: string]: string read GetValue write SetValue; default;
  20. end;
  21.  

في القسم implementation نضع تعريفات إجراءات الصنف TValueHolder كالتالي:

  1.  
  2. destructor TValueHolder.Destroy;
  3. begin
  4. FValues := nil;
  5. inherited;
  6. end;
  7.  
  8. procedure TValueHolder.SetValue(const AName, AValue: string);
  9. var
  10. Index, ValueIndex, ValuesCount: Integer;
  11. begin
  12. if FValues = nil then begin
  13. SetLength(FValues, 1);
  14. FValues[0].AName := AName;
  15. FValues[0].AValue := AValue;
  16. end
  17. else begin
  18. ValueIndex := -1;
  19. ValuesCount := Length(FValues);
  20. for Index := 0 to ValuesCount -1 do begin
  21. if SameText(FValues[Index].AName, AName) then begin
  22. ValueIndex := Index;
  23. Break;
  24. end;
  25. end;
  26. if ValueIndex = -1 then begin
  27. SetLength(FValues, ValuesCount + 1);
  28. FValues[ValuesCount].AName := AName;
  29. FValues[ValuesCount].AValue := AValue;
  30. end
  31. else
  32. FValues[ValueIndex].AValue := AValue;
  33. end;
  34. end;
  35.  
  36. function TValueHolder.GetValue(const AName: string): string;
  37. var
  38. Index, ValuesCount: Integer;
  39. begin
  40. Result := '';
  41. if FValues = nil then
  42. raise Exception.Create('The array contains no elelments');
  43.  
  44. ValuesCount := Length(FValues);
  45. for Index := 0 to ValuesCount -1 do begin
  46. if SameText(FValues[Index].AName, AName) then begin
  47. Result := FValues[Index].AValue;
  48. Exit;
  49. end;
  50. end;
  51.  
  52. raise Exception.Create('No array element with such name');
  53. end;
  54.  
  55. function TValueHolder.ValuesAsNormalArray: TValueHolderArray;
  56. var
  57. Index, ValuesCount: Integer;
  58. begin
  59. Result := nil;
  60. if FValues = nil then
  61. raise Exception.Create('The array contains no elelments');
  62.  
  63. ValuesCount := Length(FValues);
  64. SetLength(Result, ValuesCount);
  65. for Index := 0 to ValuesCount -1 do
  66. Result[Index] := FValues[Index].AValue;
  67. end;
  68.  
  69. procedure TValueHolder.Reset;
  70. begin
  71. FValues := nil;
  72. end;
  73.  

في القسم implementation أو interface نقوم بتعريف متغير من النوع TValueHolder كالتالي مثلاً:

  1.  
  2. MyArr: TValueHolder;
  3.  

ثم نقوم بإنشاء الكائن في الحدث OnCreate للـ Form

  1.  
  2. MyArr := TValueHolder.Create;
  3.  

نلاحظ أننا قمنا بتعريف الخاصية Value كخاصية مفهرسة (Indexed Property) و أن مؤشرها من النوع String بالإضافة إلى تحديدها كخاصية افتراضية (Default)، و بالتالي فإن:

  1.  
  2. // الجملة التالية:
  3. MyArr.Value['abc'] := 'This is test text';
  4. // مكافئة الجملة التالية:
  5. MyArr['abc'] := 'This is test text';
  6.  

أي أنه يمكننا إهمال اسم الخاصية Value لأنها هي الخاصية الافتراضية للكائن، و بالتالي يمكننا التعامل مع اسم الكائن كأنه مصفوفة (متبوعاً بقوسين مربعين بينهما مؤشر Index نصي).

نلاحظ هنا أن الكائن يعمل كمصفوفة غير محدودة العناصر، حيث أن الإجراء SetValue إذا لم يجد عنصراً بالاسم المطلوب فإنه يقوم بإنشاء عنصر بذلك الاسم و حفظ القيمة الجديدة فيه. بالمقابل فإن قراءة قيمة من الكائن (المصفوفة) يجب أن تشير إلى اسم عنصر موجود و إلا يحدث خطأ (كما هو مبين في الدالة GetValue).

إذن للتعامل مع الكائن:

  1.  
  2. // تخزين قيمة:
  3. MyArr['abc'] := 'This Is Text1';
  4.  
  5. // قراءة قيمة:
  6. StrVar := MyArr['abc'];
  7.  

أخيراً: ربما نحتاج إلى التعامل مع عناصر الكائن كمصفوفة اعتيادية (في حلقة For مثلاً)، و لهذا أنشأت الوظيفة ValuesAsNormalArray، و التي يمكن استعمالها كالتالي مثلاً:

  1.  
  2. procedure TFRtlForm1.Button3Click(Sender: TObject);
  3. var
  4. StrArr: TValueHolderArray;
  5. Index: Integer;
  6. begin
  7. StrArr := MyArr.ValuesAsNormalArray;
  8. for Index := 0 to Length(StrArr) - 1 do
  9. ShowMessage(StrArr[Index]);
  10. end;
  11.  

و لتصفير (إعادة ضبط) الكائن - أي حذف كل عناصره - نستعمل الوظيفة Reset للكائن:

  1.  
  2. MyArr.Reset;
  3.  

نرجو الاستفادة و السلام.

وَ قُلِ اعْمَلُوا فَسَيَرَى اللهُ عَمَلَكُمْ وَ رَسُولُهُ وَ الْمُؤْمِنُونَ

صدق الله العظيم

#4

بسم الله ما شاء الله تبارك الله

بسم الله ما شاء الله تبارك الله

بسم الله ما شاء الله تبارك الله

الله يعطيك العافية

كنت أريد أن أرد بكلاس أضعف مما ذكرت لأني شاهد شبيه بمثله في الوحدة hashes.pas ، ولكنك يا أخي najy_z قطعت قول كل خطيب .

ولو وافيت ربك دون ذنب *** وناقشك الحساب إذاً هلكتا

ولم يظلمك في عملٍ ولكن *** عسير أن تقوم بما حملتا

#5

لعله يغني عن الوظيفة ValuesAsNormalArray وضع خاصية تقرأ من FValues مباشرة .

TValueHolderArray = array of TValueHolderMap;

.....

public

		property data : TValueHolderArray read FValues;

ثم يكون الإستخدام كالتالي :

procedure TForm1.btnViewClick(Sender: TObject);
var I : integer;
begin
  listbox1.Clear;		  
  for I := 0 to MyArr.GetCount - 1 do
	ListBox1.Items.Add(MyArr.data.AName+':='+MyArr.data.AValue );
end;

ولو وافيت ربك دون ذنب *** وناقشك الحساب إذاً هلكتا

ولم يظلمك في عملٍ ولكن *** عسير أن تقوم بما حملتا

#6

السلام عليكم...

الفائدة من طرح المواضيع و الأكواد هي المناقشة و الوصول لأفضل الحلول. و طالما وصلنا إلى الفكرة فإنه يمكننا تعديلها و تطويرها بما يتناسب مع احتياجاتنا. أشكرك و أرجو لك التوفيق.

و السلام.

وَ قُلِ اعْمَلُوا فَسَيَرَى اللهُ عَمَلَكُمْ وَ رَسُولُهُ وَ الْمُؤْمِنُونَ

صدق الله العظيم

#7

السلام عليكم

أولا شكرا على الطريقة التي قدمتها أخي ناجي ولكن لديّا سؤال لو سمحت هل الكلام الذي ذكرته أنا دقيق مئة بالمئة أم لايزال هنالك طريقة للإلتفاف بإستخدام مصفوفة

#8

السلام عليكم ورحمة الله وبركاته

أشكرك يا أخي على إيصال الفكرة ولكن لا مانع من إضافة بعض الفوائد لأمثالي البعيدين العهد عن الدلفي ، لأني الآن راجع وأحاول تجميع أوراقي وأتآلف معها أكثر .

حاولت أن أصمم الكلاس من جديد مستفيد من الأفكار الموجودة في مشاركة الأخ ناجي . وعملت واجهة اختبارية للكائن ، كما في الصورة في المرفقات ، لأن هذا الكائن هو الذي أريده وأبني عليه بعض التطبيقات .

هذا الكلاس الذي عملته هو لأجل تخزين النصوص، كما عمله الأخ الموفق ناجي، ويعمل البرنامج معه تماماً بدون مشاكل .

وظهر لدي استفسار وهو أني أريد الاشتقاق منه مكوّن خاص بالأرقام ليكون الكود كالتالي :

var 
	level : TIntConfig;
	permit : TIntConfig;
//========
level:= TIntConfig.Create();
permit:= TIntConfig.Create();

// USAGE
if (permit['mytable.view'] > level['username']) then
	/// Access forbidden

فإذا تمت عملية الاشتقاق لن يتغير من الدوال إلا اثنين فقط وهما SetItem و GetItem ، لأن سائر الدوال الباقية ليس لها علاقة بنوع حقل المصفوفة.

ولكن لم تنجح عملية الاشتقاق ولا أريد إعادة كتابة الكائن كله من جديد لأجل الأرقام

تجدون الاختبار لأجل الأرقام في unit1.pas كتعليقات لأجل تسهيل الفهم .

والله الموفق .

post-71437-1254629581_thumb.gif

testConfig.zip

ولو وافيت ربك دون ذنب *** وناقشك الحساب إذاً هلكتا

ولم يظلمك في عملٍ ولكن *** عسير أن تقوم بما حملتا

#9

مرحبا أخي أبو محمد لكن على العكس منكم نستفيد إن شاء

التعديل يجب أن يتم على السجل أي إنشاء سجل أخر جرب هذا المرفق وإن شاء الله بيمشي الحال

Test.rar

#10

الحمد لله وكفى

محاولة جيدة أخي حسين.

ولكن المشكلة هي أن السجل الجديد في الصنف الابن لا تتعامل معه دوال وإجراءات الصنف الأب إلا بعد إعادة كتابتها من جديد في الصنف الأبن ، لأنها (في الأب) تعتبر الحقل والخصائص المرتبطة به في الصنف الابن أمر لا يسمح بالوصول إليه . ( خطأ في الوصول إلى الذاكرة). مع أن الحقل والخصائص تحمل نفس الاسم .

هذه هي المشكلة بالتحديد.

فمثلاً في المرفق إجراءات في الصنف الأب يجب ألا يعاد كتابتها في الأصناف الأبناء لأنه لا علاقة لها بنوع القيمة التي يحملها الحقل هي تتعامل مع الاسم فقط وهو نص في الجميع . وهذه الإجراءات هي remove, indexOf, seve, ...

بينما الدالة reset تعمل والله أعلم.

هذا المرفق من جديد مع بعض التحسين ، وأريد النقاش يكون حوله، لأن فيه الدوال التي المفترض أن يرثها الكائن الابن ولكنها تتبرأ من حقوله .

testConfig.zip

تم تعديل هذه المشاركة بواسطة أبو محمد اللحياني في 5 أكتوبر 2009 في 15:20

ولو وافيت ربك دون ذنب *** وناقشك الحساب إذاً هلكتا

ولم يظلمك في عملٍ ولكن *** عسير أن تقوم بما حملتا

#11

السلام عليكم...

هذه هي محتويات الوحدة Configs بعد إجراء التعديلات عليها. و هي الآن تدعم الأنواع String و Integer و Real (أو Float) و Boolean و TDateTime. و يمكن بتعديلات بسيطة أن تدعم أنواعاً أخرى. مع ملاحظة أني لم أقم بمراجعة الوظيفتين Reset و Remove.

ملاحظة: كتبت بعض الملاحظات باللغة الإنجليزية - و ليس بالعربي - تجنباً لعدم إظهار النص العربي بشكل غير سليم.

المشروع أيضاً موجود في المرفقات.

  1.  
  2. unit Configs;
  3.  
  4. interface
  5.  
  6. uses SysUtils, Classes, Dialogs, IniFiles, DateUtils;
  7.  
  8. Type
  9. TConfigDataType = (ctsStr, ctsInt, ctsReal, ctsBool, ctsDate);
  10.  
  11. TConfigRec = Record
  12. Key : string[50];
  13. CDF: TConfigDataType;
  14. case TConfigDataType of
  15. ctsStr: (AsString: string[255]);
  16. ctsInt: (AsInteger: Integer);
  17. ctsReal: (AsFloat: Real);
  18. ctsBool: (AsBoolean: Boolean);
  19. ctsDate: (AsDateTime: TDateTime);
  20. end;
  21.  
  22. TConfigArr = array of TConfigRec;
  23. TConfigFile = file of TConfigRec;
  24.  
  25. // Base configuration class (TConfigBase):
  26. // NOTE: DO NOT instantiate this class (i.e. DO NOT create objects from it).
  27. TConfigBase = class
  28. Private
  29. FItems: TConfigArr;
  30. FCount: Integer;
  31.  
  32. function GetCount: Integer;
  33. procedure SetItemValue(const Index: Integer; const Value: Variant; ADataType: TConfigDa
    taType);
  34. function GetItem(const Key: string; ADataType: TConfigDataType): Variant;
  35. procedure SetItem(const Key: string; const Value: Variant; ADataType: TConfigDataType);
  36. procedure LoadFromIni(const FileData: string; const isReset: Boolean; ADataType: TConfi
    gDataType);
  37.  
  38. public
  39. procedure Reset;
  40. procedure Load(const FileData: string; const isReset: Boolean = True);
  41. procedure Save(const FileData: string);
  42. procedure Remove(const Key: string);
  43.  
  44. property Count: Integer read GetCount;
  45. property ItemOf : TConfigArr read FItems;
  46. end;
  47.  
  48. // Derived classes:
  49. // TStrConfig: for strings.
  50. TStrConfig = class(TConfigBase)
  51. Private
  52. function GetItem(const Key: string): string;
  53. procedure SetItem(const Key: string; const Value: string);
  54. public
  55. procedure LoadIni(const FileData: string; const isReset: Boolean = True);
  56. property Items[const Key: string]: string read GetItem write SetItem; default;
  57. end;
  58. //---------------------------------------
  59.  
  60. // TIntConfig: for integers.
  61. TIntConfig = class(TConfigBase)
  62. Private
  63. function GetItem(const Key: string): Integer;
  64. procedure SetItem(const Key: string; const Value: Integer);
  65. public
  66. procedure LoadIni(const FileData: string; const isReset: Boolean = True);
  67. property Items[const Key: string]: Integer read GetItem write SetItem; default;
  68. end;
  69. //---------------------------------------
  70.  
  71. //TFloatConfig: for reals:
  72. TFloatConfig = class(TConfigBase)
  73. Private
  74. function GetItem(const Key: string): Real;
  75. procedure SetItem(const Key: string; const Value: Real);
  76. public
  77. procedure LoadIni(const FileData: string; const isReset: Boolean = True);
  78. property Items[const Key: string]: Real read GetItem write SetItem; default;
  79. end;
  80. //---------------------------------------
  81.  
  82. //TBooleanConfig: for booleans:
  83. TBooleanConfig = class(TConfigBase)
  84. Private
  85. function GetItem(const Key: string): Boolean;
  86. procedure SetItem(const Key: string; const Value: Boolean);
  87. public
  88. procedure LoadIni(const FileData: string; const isReset: Boolean = True);
  89. property Items[const Key: string]: Boolean read GetItem write SetItem; default;
  90. end;
  91. //---------------------------------------
  92.  
  93. //TDateTimeConfig: for TDateTime:
  94. TDateTimeConfig = class(TConfigBase)
  95. Private
  96. function GetItem(const Key: string): TDateTime;
  97. procedure SetItem(const Key: string; const Value: TDateTime);
  98. public
  99. procedure LoadIni(const FileData: string; const isReset: Boolean = True);
  100. property Items[const Key: string]: TDateTime read GetItem write SetItem; default;
  101. end;
  102. //---------------------------------------
  103.  
  104. const
  105.  
  106. // NOTE: if the type TConfigDataType (above) changed, values assigned to
  107. // this constant should be changed accordingly.
  108. CONFIG_DATA_SET = [ctsStr, ctsInt, ctsReal, ctsBool, ctsDate];
  109.  
  110. implementation
  111.  
  112. // TConfigBase class members:
  113. //===========================
  114. // A. Private procs and funcs:
  115. function TConfigBase.GetCount: Integer;
  116. begin
  117. Result := 0;
  118. if FCount > 0 then
  119. Result := FCount;
  120. end;
  121.  
  122. procedure TConfigBase.SetItemValue(const Index: Integer; const Value: Variant; ADataType: TConf
    igDataType);
  123. begin
  124. case ADataType of
  125. ctsStr: FItems[Index].AsString := Value;
  126. ctsInt: FItems[Index].AsInteger := Value;
  127. ctsReal: FItems[Index].AsFloat := Value;
  128. ctsBool: FItems[Index].AsBoolean := Value;
  129. ctsDate: FItems[Index].AsDateTime := Value;
  130. else
  131. raise Exception.Create('The given data type is not supported now');
  132. end;
  133. end;
  134.  
  135. function TConfigBase.GetItem(const Key: string; ADataType: TConfigDataType): Variant;
  136. var
  137. I, FoundIndex: integer;
  138. begin
  139. if FItems = nil then
  140. raise Exception.Create('The array contains no elements');
  141.  
  142. FoundIndex := -1;
  143. Result := '';
  144. for I := 0 to Count - 1 do begin
  145. if FItems[i].key = Key then begin
  146. FoundIndex := I;
  147. Break;
  148. end;
  149. end;
  150.  
  151. if FoundIndex = -1 then
  152. raise Exception.Create('The name ' + Key + ' does not exist among the items')
  153. else begin
  154. case ADataType of
  155. ctsStr: Result := FItems[FoundIndex].AsString;
  156. ctsInt: Result := FItems[FoundIndex].AsInteger;
  157. ctsReal: Result := FItems[FoundIndex].AsFloat;
  158. ctsBool: Result := FItems[FoundIndex].AsBoolean;
  159. ctsDate: Result := FItems[FoundIndex].AsDateTime;
  160. else
  161. raise Exception.Create('The given data type is not supported now');
  162. end;
  163. end;
  164. end;
  165.  
  166. procedure TConfigBase.SetItem(const Key: string; const Value: Variant; ADataType: TConfigDataTy
    pe);
  167. var
  168. I: integer;
  169. begin
  170. if not (ADataType in CONFIG_DATA_SET) then
  171. raise Exception.Create('The given data type is not supported now');
  172.  
  173. if FItems = nil then begin
  174. SetLength(FItems, 1);
  175. FItems[0].Key := Key ;
  176. FItems[0].CDF := ADataType ;
  177. SetItemValue(0, Value, ADataType);
  178. Inc(FCount);
  179. Exit;
  180. end;
  181.  
  182. for I := 0 to FCount - 1 do begin
  183. if FItems[I].Key = Key then begin
  184. FItems[I].CDF := ADataType;
  185. SetItemValue(I, Value, ADataType);
  186. Exit;
  187. end;
  188. end;
  189.  
  190. SetLength(FItems, FCount + 1);
  191. FItems[FCount].Key := Key ;
  192. FItems[FCount].CDF := ADataType ;
  193. SetItemValue(FCount, Value, ADataType);
  194. Inc(FCount);
  195. end;
  196.  
  197. procedure TConfigBase.LoadFromIni(const FileData: string; const isReset: Boolean; ADataType: TC
    onfigDataType);
  198. var
  199. I, N: Integer;
  200. objIni: TIniFile;
  201. Sections, KeyList :TStringList;
  202. begin
  203. if isReset then self.Reset;
  204.  
  205. if FileExists(FileData) Then begin
  206. try
  207. Sections :=TStringList.Create;
  208. KeyList := TStringList.Create;
  209. objIni := TIniFile.Create(FileData);
  210. objIni.ReadSections(Sections);
  211. for I := 0 to Sections.Count - 1 do begin
  212. objIni.ReadSection(Sections.ValueFromIndex[I], KeyList);
  213. for N := 0 to KeyList.Count - 1 do begin
  214. case ADataType of
  215. ctsStr: SetItem(KeyList[N], objIni.ReadString(Sections.ValueFromIndex[I
    ], KeyList[N], ''), ctsStr);
  216. ctsInt: SetItem(KeyList[N], objIni.ReadInteger(Sections.ValueFromIndex[
    I], KeyList[N], 0), ctsInt);
  217. ctsReal: SetItem(KeyList[N], objIni.ReadFloat(Sections.ValueFromIndex[I
    ], KeyList[N], 0), ctsReal);
  218. ctsBool: SetItem(KeyList[N], objIni.ReadBool(Sections.ValueFromIndex[I]
    , KeyList[N], False), ctsBool);
  219. ctsDate: SetItem(KeyList[N], objIni.ReadDateTime(Sections.ValueFromInde
    x[I], KeyList[N], EncodeDateTime(1899, 12, 30, 0, 0, 0, 0)), ctsDate);
  220. end;
  221. end;
  222. end;
  223. finally
  224. Sections.Free;
  225. KeyList.Free;
  226. objIni.Free;
  227. end; // try
  228. end // end if exists file
  229. else
  230. ShowMessage('The file ' + FileData + ' is not found.');
  231. end;
  232.  
  233.  
  234. // B. Public procs and funcs (Methods):
  235. procedure TConfigBase.Reset;
  236. begin
  237. FItems := nil;
  238. FCount := 0;
  239. end;
  240.  
  241. procedure TConfigBase.Load(const FileData: string; const isReset: Boolean);
  242. var
  243. F: TConfigFile;
  244. cfg: TConfigRec;
  245. begin
  246. if isReset then self.Reset;
  247.  
  248. if FileExists(fileData) Then begin
  249. try
  250. AssignFile(F, FileData) ;
  251. FileMode := fmOpenRead;
  252. System.Reset(F) ;
  253. while not Eof(F) do begin
  254. System.Read(F, cfg);
  255. case cfg.CDF of
  256. ctsStr: SetItem(cfg.key, cfg.AsString, ctsStr);
  257. ctsInt: SetItem(cfg.key, cfg.AsInteger, ctsInt);
  258. ctsReal: SetItem(cfg.key, cfg.AsFloat, ctsReal);
  259. ctsBool: SetItem(cfg.key, cfg.AsBoolean, ctsBool);
  260. ctsDate: SetItem(cfg.key, cfg.AsDateTime, ctsDate);
  261. end;
  262. end;
  263. finally
  264. CloseFile(F) ;
  265. end;
  266. end // end if exists file
  267. else
  268. ShowMessage('The file ' + FileData + ' is not found.');
  269. end;
  270.  
  271. procedure TConfigBase.Save(const FileData: String);
  272. var
  273. F : TConfigFile;
  274. I : integer;
  275. begin
  276. try
  277. AssignFile(F, FileData) ;
  278. FileMode := fmOpenWrite;
  279. ReWrite(F);
  280. for I := 0 to FCount - 1 do
  281. Write(F, FItems[I]);
  282. finally
  283. CloseFile(F) ;
  284. end;
  285. end;
  286.  
  287. procedure TConfigBase.Remove(const Key: string);
  288. var
  289. P, I: Integer;
  290. begin
  291. P := -1;
  292. for I := 0 to FCount - 1 do begin
  293. if FItems[i].Key = Key then begin
  294. P := I;
  295. Break;
  296. end;
  297. end;
  298.  
  299. if P = -1 then
  300. raise Exception.Create('The name: ' + Key + ' does not exist among the items');
  301.  
  302. if P = FCount - 1 then begin // key in last FItems
  303. Setlength(FItems, FCount -1);
  304. FCount := FCount - 1;
  305. Exit;
  306. end;
  307.  
  308. for I := P to FCount - 2 do
  309. FItems[I] := FItems[I + 1];
  310.  
  311. FCount := FCount - 1;
  312. SetLength(FItems, FCount - 1); //now we have one element less
  313. end;
  314. // End of TConfigBase class
  315. // =============================================================================
  316.  
  317.  
  318. // ============================ Derived Classes ================================
  319.  
  320. // TStrConfig class:
  321. // =================
  322. function TStrConfig.GetItem(const Key: string): string;
  323. begin
  324. Result := string(inherited GetItem(Key, ctsStr));
  325. end;
  326.  
  327. procedure TStrConfig.SetItem(const Key: string; const Value: string);
  328. begin
  329. inherited SetItem(Key, Value, ctsStr);
  330. end;
  331.  
  332. procedure TStrConfig.LoadIni(const FileData: string; const isReset: Boolean = True);
  333. begin
  334. LoadFromIni(FileData, isReset, ctsStr); // inherited.
  335. end;
  336. // =============================================================================
  337.  
  338.  
  339. // TIntConfig:
  340. // =================
  341. function TIntConfig.GetItem(const Key: string): Integer;
  342. begin
  343. Result := Integer(inherited GetItem(Key, ctsInt));
  344. end;
  345.  
  346. procedure TIntConfig.SetItem(const Key: string; const Value: Integer);
  347. begin
  348. inherited SetItem(Key, Value, ctsInt);
  349. end;
  350.  
  351. procedure TIntConfig.LoadIni(const FileData: string; const isReset: Boolean = True);
  352. begin
  353. LoadFromIni(FileData, isReset, ctsInt);
  354. end;
  355. // =============================================================================
  356.  
  357. //(ctsStr, ctsInt, ctsReal, ctsBool, ctsDate);
  358.  
  359. // TFloatConfig:
  360. // =================
  361. function TFloatConfig.GetItem(const Key: string): Real;
  362. begin
  363. Result := Real(inherited GetItem(Key, ctsReal));
  364. end;
  365.  
  366. procedure TFloatConfig.SetItem(const Key: string; const Value: Real);
  367. begin
  368. inherited SetItem(Key, Value, ctsReal);
  369. end;
  370.  
  371. procedure TFloatConfig.LoadIni(const FileData: string; const isReset: Boolean = True);
  372. begin
  373. LoadFromIni(FileData, isReset, ctsReal);
  374. end;
  375. // =============================================================================
  376.  
  377.  
  378. // TBooleanConfig:
  379. // =================
  380. function TBooleanConfig.GetItem(const Key: string): Boolean;
  381. begin
  382. Result := Boolean(inherited GetItem(Key, ctsBool));
  383. end;
  384.  
  385. procedure TBooleanConfig.SetItem(const Key: string; const Value: Boolean);
  386. begin
  387. inherited SetItem(Key, Value, ctsBool);
  388. end;
  389.  
  390. procedure TBooleanConfig.LoadIni(const FileData: string; const isReset: Boolean = True);
  391. begin
  392. LoadFromIni(FileData, isReset, ctsBool);
  393. end;
  394. // =============================================================================
  395.  
  396.  
  397. // TDateTimeConfig:
  398. // =================
  399. function TDateTimeConfig.GetItem(const Key: string): TDateTime;
  400. begin
  401. Result := TDateTime(inherited GetItem(Key, ctsDate));
  402. end;
  403.  
  404. procedure TDateTimeConfig.SetItem(const Key: string; const Value: TDateTime);
  405. begin
  406. inherited SetItem(Key, Value, ctsDate);
  407. end;
  408.  
  409. procedure TDateTimeConfig.LoadIni(const FileData: string; const isReset: Boolean = True);
  410. begin
  411. LoadFromIni(FileData, isReset, ctsDate);
  412. end;
  413. // =============================================================================
  414.  
  415. end.
  416.  

الصنف (Class) الأساسي هو TConfigBase و هو الذي يحتوي على معظم (بل كل) الكود المطلوب، لكن لا يجب أن ننشئ منه كائنات (Objects) و إنما يتم إنشاء الكائنات من الأصناف المشتقة منه: TStrConfig و TIntConfig و TFloatConfig و TBooleanConfig و TDateTimeConfig، بالإضافة إلى أية أصناف أخرى يتم اشتقاقها منه (أي الصنف TConfigBase).

نلاحظ أن عملية الاشتقاق سهلة: إعادة كتابة الدالة GetItem و الإجرائين SetItem و LoadIni. و نلاحظ أن إعادة كتابة هذه الإجراءات لا تعني إعادة كامل الكود، بل تحتاج كل منها إلى سطر واحد فقط لأنها تعتمد على استدعاء الإجراءات الأصلية الموروثة (inherited) مع تمرير نوع البيانات. بالإضافة إلى تعريف الخاصية Items لكل صنف مشتق حسب نوعه.

هناك نقطة واحدة هنا قد تتعارض مع مفاهيم الـ (Object Oriented Programming = OOP) و هي أنه إذا أردنا إنشاء أصناف مشتقة جديدة (غير الموجودة حالياً في الكود) فإننا نحتاج إلى إجراء بعض التعديلات في بعض إجراءات النوع الأساس TConfigBase، أو - إذا أمكن - أن نقوم بنوع من التحويل بين نوع البيانات الجديد و أحد الأنواع المعرفة سابقاً حتى لا نقوم بتغيير الكود الأصلي للصنف TConfigBase

TestConfig02.rar

على أية حال نرجو الاستفادة و السلام.

وَ قُلِ اعْمَلُوا فَسَيَرَى اللهُ عَمَلَكُمْ وَ رَسُولُهُ وَ الْمُؤْمِنُونَ

صدق الله العظيم

#12

السلام عليكــم ورحمـة الله وبركاتــه ،،

شكرا لك أخي ناجي حقيقتا كنت أفكر بإستخدام توابع مثل AsInteger كما في المكون ClientDataSet لم أكن أعرف أنه يمكن إستخدام case of عند التصريح

شكرا ثانية

#13

أعتذر عن تأخري عن الرد لانقطاع الإنترنت !!

في الحقيقة يا أخ ناجي امتعتنا بفوائدك ، لا حرمك الله الأجر .

وإن كنت أنا أتوقف كثيراً في استخدام النوع Variant ، لأنهم قالوا عنه أنه بطئء ، ولذلك أحببت إستخدام ( ملف ، ومصفوفة ، وسجل) كلها من نوع واحد لأجل التهرب من تحويل الأنواع في كل عملية إسناد واستخراج.

فقط لإثراء الموضوع ولإختصار الكود، ( مع أني محرج من الأخ الموفق ناجي من كثرة التغيير !! في الكود )::

لماذا لا يكون المتغير ADataType حقل في الكائن الأساس ؟

Private
	 FDataType : TConfigDataType;
public
	property DataType : TConfigDataType read FDataType;
//----------------------------------
constructor TConfigBase.Create(ADataType: TConfigDataType);
begin
  FDataType := ADataType;
end;
// -----------------------------
var
  content: TConfigBase;
//---------
content := TConfigBase.Create(ctsStr);
//---------------------------
case content.DataType of 
	ctsStr : AValue := content.ItemOf.AsString;
	ctsInt : AValue := IntToStr(content.ItemOf.AsInteger);
	ctsReal : AValue := FloatToStr(content.ItemOf.AsFloat);
	ctsBool : AValue := BoolToStr(content.ItemOf.AsBoolean);
	ctsDate : AValue := DateTimeToStr(content.ItemOf.AsDateTime);
end;

ثم غيِّر ما يلزم .

أشكر الجميع والله الموفق

والسلام عليكم ورحمة الله وبركاته .

ولو وافيت ربك دون ذنب *** وناقشك الحساب إذاً هلكتا

ولم يظلمك في عملٍ ولكن *** عسير أن تقوم بما حملتا

#14

في الحقيقة الاقتراح السابق لا يغني عن الاشتقاق من الصنف الأساس لأصناف تدعم نوع المتغير

فإذا كنا نريد البساطة التي سألنا عنها أول الموضوع لا بد من اشتقاق كما فعل الأخ ناجي ولا يكتفى باستخدام الصنف TConfigBase كما في مشاركتي الأخيرة .

والله أعلم .

ولو وافيت ربك دون ذنب *** وناقشك الحساب إذاً هلكتا

ولم يظلمك في عملٍ ولكن *** عسير أن تقوم بما حملتا

مواضيع مشابهة