RTTI: هل يمكنني الحصول على نوع بالاسم؟
-
13-09-2019 - |
سؤال
بالنظر إلى سلسلة نصية تحتوي على اسم نوع، هل هناك طريقة للحصول على النوع المناسب نفسه؟
أنا أتطلع إلى القيام بشيء مثل هذا:
type
TSomeType<T> = class
// yadda yadda
end;
procedure DoSomething;
var
obj : TObject;
begin
o := TSomeType<GetTypeByName('integer')>.Create;
// do stuff with obj
end;
لقد بحثت في العديد من التفسيرات RTTI عبر الإنترنت ونظرت من خلال وحدات Delphi ولا ترى ما أبحث عنه. هل هذا ممكن؟
المحلول
لا، الجنراء مجمعة تماما.
نصائح أخرى
تتمتع وحدة RTTI الجديدة في Delphi 2010 بأي طريقة لاسترجاع الأنواع المعلنة في قسم الواجهة من الوحدات. لأي نوع معين، يمثله TRttiType
المثال، TRttiType.QualifiedName
إرجاع العقار اسما يمكن استخدامه TRttiContext.FindType
في وقت لاحق لاسترداد النوع. الاسم المؤهل هو اسم الوحدة الكاملة (بما في ذلك مساحات الأسماء، إذا كانت موجودة)، متبوعة ب "."، تليها اسم النوع الكامل (بما في ذلك الأنواع الخارجية إذا كانت متداخلة).
لذلك، يمكنك استرداد تمثيل للنوع الصحيحة (في شكل TRttiType
) مع context.FindType('System.Integer')
.
لكن هذه الآلية لا يمكن استخدامها لاسترداد إنشاء إنشاءات من الأنواع العامة التي لم يتم إنشاء مثيل لها في وقت الترجمة؛ إن إنشاء مثيل في وقت التشغيل يتطلب جيل رمز وقت التشغيل.
يمكنك دائما تسجيل أنواعك في نوع من السجل (تدار بواسطة قائمة أو قاموس سلسلة) وإنشاء وظيفة المصنع ثم إرجاع الكائن المناسب. لسوء الحظ، عليك أن تعرف مقدما من أنواع التي ستحتاجها. شيء مشابه ل Delphi وظائف تسجيل و FindClass (في وحدة الطبقات). تفكيري هو وضع نوع القالب عام في القائمة مباشرة.
مثال على الاستخدام المحتمل:
RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);
if FindCustomType('Integer') <> nil then
O := FindCustomType('Integer').Create;
تعديل: فيما يلي تطبيق بسيط محدد باستخدام TDICTIONARY من Generics.collections للتعامل مع مساحة التسجيل ... سأترك استخراج هذا إلى أساليب مفيدة كممارسة بسيطة للقارئ.
var
o : TObject;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) then
ShowMessage(o.ClassName);
end;
تحرير آخر: كنت أعطي هذا التفكير في الليلة الماضية، واكتشفت تقنية أخرى يمكنك دمجها في هذا المفهوم. واجهات. هنا هو مثال لا شيء سريع، ولكن يمكن توسيعها بسهولة:
TYPE
ITest = interface
['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
end;
TIntfList<t> = class(TList<T>,ITest)
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
procedure TForm1.Button7Click(Sender: TObject);
var
o : TObject;
fTestIntf : ITest;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TIntfList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) and Supports(o,ITest,fTestIntf) then
ShowMessage(o.ClassName);
end;
بالطبع، يجب عليك تطبيق أساليب QueryInterface، _ADDREF و _Release وتوسيع الواجهة للقيام بشيء أكثر فائدة.
إذا نسيت الأجهزة والأنواع الأساسية، فستكون وظيفة "RegisterClass" مفيدة. لكنها لا تعمل على الأجهزة أو الأنواع الأساسية.