استبطان النوع
في مجال الحوسبة ، يُعرف استبطان النوع بأنه قدرة البرنامج على فحص نوع أو خصائص كائن ما أثناء التشغيل . وتمتلك بعض لغات البرمجة هذه القدرة.
لا ينبغي الخلط بين الاستبطان والانعكاس ، الذي يتجاوز ذلك خطوةً أخرى ، وهو قدرة البرنامج على معالجة البيانات الوصفية والخصائص والوظائف الخاصة بكائن ما أثناء التشغيل. وتمتلك بعض لغات البرمجة هذه القدرة أيضاً (مثل جافا ، وبايثون ، وجوليا ، وجو ).
أمثلة
لغة سي++
تدعم لغة C++ فحص أنواع البيانات عبر معلومات نوع وقت التشغيل (RTTI) typeid والكلمات المفتاحية dynamic cast . dynamic_castيمكن استخدام هذا التعبير لتحديد ما إذا كان كائن معين ينتمي إلى فئة مشتقة معينة. على سبيل المثال:
Person * p = dynamic_cast < Person *> ( obj ); if ( p ) { p -> walk (); }typeidيسترجع هذا المعامل std::type_infoكائنًا يصف النوع الأكثر اشتقاقًا من كائن ما:
إذا كان نوع الكائن ( Person ) يساوي نوع الكائن ( * obj )، فقم بتسلسل الكائن ( obj ) .سي شارب
في لغة C#، يمكن إجراء الاستبطان باستخدام isالكلمة المفتاحية. على سبيل المثال:
إذا كان ( obj هو Person ) { // افعل ما تريد }أوبجكتيف سي
في لغة Objective-C ، على سبيل المثال، يوفر كل من الكائن العام Object و NSObject (في Cocoa / OpenStep ) دالةisMemberOfClass: تُرجع القيمة true إذا كان الوسيط المُمرر إليها نسخة من الفئة المُحددة. isKindOfClass:وبالمثل، تُرجع الدالة القيمة true إذا كان الوسيط يرث من الفئة المُحددة.
على سبيل المثال، لنفترض أن لدينا فئة Appleوفئة Orangeترث من فئة أخرى Fruit.
الآن، في هذه eatالطريقة يمكننا أن نكتب
- ( void ) eat: ( id ) sth { if ([ sth isKindOfClass : [ Fruit class ]]) { // نحن نأكل فاكهة بالفعل، لذا نستمر if ([ sth isMemberOfClass : [ Apple class ]]) { eatApple ( sth ); } else if ([ sth isMemberOfClass : [ Orange class ]]) { eatOrange ( sth ); } else { error (); } } else { error (); } }الآن، عند eatاستدعاء الدالة باستخدام كائن عام (كائن id)، ستتصرف الدالة بشكل صحيح اعتمادًا على نوع الكائن العام.
أوبجكت باسكال
يُعدّ فحص أنواع البيانات جزءًا أساسيًا من لغة Object Pascal منذ الإصدار الأول من Delphi، التي تستخدم هذه التقنية بكثافة في تصميم النماذج المرئية. في Object Pascal، تنحدر جميع الفئات من الفئة الأساسية TObject، التي تُنفّذ وظائف فحص أنواع البيانات الأساسية. يُمكن الإشارة إلى اسم أي فئة في الكود لأغراض فحص أنواع البيانات؛ حيث يُنفّذ مُعرّف اسم الفئة كمؤشر إلى بياناتها الوصفية، والتي يُمكن تعريفها واستخدامها كمتغير من النوع TClass. تتضمن اللغة عامل التشغيل is ، لتحديد ما إذا كان الكائن ينتمي إلى فئة مُحددة أو ينحدر منها، وعامل التشغيل as ، الذي يُوفّر تحويلًا مُدقّقًا للأنواع، بالإضافة إلى العديد من توابع TObject. تقليديًا، لا يُدعم الفحص المُعمّق (تعداد الحقول والتوابع) إلا للكائنات المُعرّفة في حالة $M+ (pragma)، والتي عادةً ما تكون TPersistent، وللرموز المُعرّفة في القسم المنشور فقط. وقد وسّع Delphi 2010 نطاق هذا الدعم ليشمل جميع الرموز تقريبًا.
procedure Form1.MyButtonOnClick ( Sender : TObject ) ; var aButton : TButton ; SenderClass : TClass ; begin SenderClass := Sender.ClassType ; // يُعيد مؤشر فئة المُرسِل إذا كان المُرسِل من نوع TButton then begin aButton := sender as TButton ; EditBox.Text : = aButton.Caption ; // خاصية يمتلكها الزر، ولكن لا توجد خاصية في الكائنات العامة end else begin EditBox.Text : = Sender.ClassName ; // يُعيد اسم فئة المُرسِل كسلسلة نصية end ; end ;جافا
أبسط مثال على استبطان النوع في جافا هو عامل التشغيل instanceof[ 1 ] . instanceofيحدد هذا العامل ما إذا كان كائن معين ينتمي إلى فئة معينة (أو فئة فرعية من تلك الفئة، أو فئة تُنفذ تلك الواجهة). على سبيل المثال:
إذا كان الكائن من نوع Person ، فسيتم استدعاء الدالة walk ( ) .الفئة java.lang.Class[ 2 ] هي أساس الاستبطان الأكثر تقدماً.
على سبيل المثال، إذا كان من المرغوب فيه تحديد الفئة الفعلية للكائن (بدلاً من تحديد ما إذا كان عضوًا في فئة معينة )، Object.getClass()فيمكن Class.getName()استخدامه:
System.out.println ( obj.getClass ( ) . getName ( ) ) ;PHP
في لغة PHP، يمكن إجراء الاستبطان باستخدام instanceofعامل التشغيل. على سبيل المثال:
إذا كان الكائن من نوع Person ، فقم بما تريد .بيرل
يمكن تحقيق الاستبطان باستخدام وظائف refو isaفي لغة بيرل .
يمكننا أن نتفحص الفئات التالية وحالاتها المقابلة:
package Animal ; sub new { my $class = shift ; return bless {}, $class ; }حزمة Dog ؛ استخدم القاعدة 'Animal' ؛package main ; my $animal = Animal -> new (); my $dog = Dog -> new ();استخدام:
اطبع "هذا حيوان.\n" إذا كان المرجع $animal يساوي 'Animal' ؛ اطبع "الكلب حيوان.\n" إذا كان $dog- > isa ( 'Animal' )؛بروتوكول الكائن الميتا
يمكن تحقيق استبطان أكثر قوة في لغة بيرل باستخدام نظام كائنات Moose [ 3 ] وبروتوكول Class::MOPالكائنات الوصفية ؛ [ 4 ] على سبيل المثال، يمكنك التحقق مما إذا كان كائن معين يقوم بدور X :
إذا كان ( $object -> meta -> does_role ( "X" )) { # قم بشيء ما ... }هذه هي الطريقة التي يمكنك من خلالها سرد الأسماء الكاملة لجميع الطرق التي يمكن استدعاؤها على الكائن، بالإضافة إلى الفئات التي تم تعريفها فيها:
for my $method ( $object -> meta -> get_all_methods ) { print $method -> fully_qualified_name , "\n" ; }بايثون
الطريقة الأكثر شيوعًا للاستبطان في بايثون هي استخدام dirالدالة لعرض تفاصيل سمات الكائن. على سبيل المثال:
class Foo : def __init__ ( self , val : int ) -> None : self . x = valدالة get_x ( self ) -> int : return self . xprint ( dir ( Foo ( 5 ))) # يطبع ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__firstlineno__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__static_attributes__', '__str__', '__subclasshook__', '__weakref__', 'get_x', 'x']typeكما يمكن استخدام الدوال المدمجة isinstanceلتحديد ماهية الكائن ، بينما hasattrيمكن استخدام الدوال الأخرى لتحديد وظيفته . على سبيل المثال:
a : Foo = Foo ( 10 ) b : Bar = Bar ( 11 ) print ( type ( a )) # يطبع <type 'Foo'> isinstance ( a , Foo ) # يطبع True print ( isinstance ( a , type ( a ))) # يطبع True print ( isinstance ( a , type ( b ))) # يطبع False print ( hasattr ( a , 'bar' )) # يطبع Trueعلاوة على ذلك، يمكن الوصول إلى تلميحات أنواع بايثون في وقت التشغيل ويمكن استخدامها بواسطة مكتبات مثل Pydantic للتسلسل والتحقق من الصحة، و FastAPI لحقن التبعية وإنشاء مخطط API.
روبي
يُعدّ فحص أنواع البيانات ميزة أساسية في لغة روبي . في روبي، توفر فئة Object (الفئة الأصلية لكل فئة) Object#instance_of?طرقًا Object#kind_of?للتحقق من نوع الكائن. تُرجع الطريقة الأخيرة القيمة true عندما يكون الكائن المُرسَل إليه الرسالة كائنًا من فئة فرعية من الفئة المعنية. على سبيل المثال، انظر إلى نموذج الكود التالي (يمكنك تجربته مباشرةً باستخدام واجهة روبي التفاعلية ):
$ irb irb(main):001:0> A = Class . new => A irb(main):002:0> B = Class . new A => B irb(main):003:0> a = A . new => #<A:0x2e44b78> irb(main):004:0> b = B . new => #<B:0x2e431b0> irb(main):005:0> a . instance_of? A => true irb(main):006:0> b . instance_of? A => false irb(main):007:0> b . kind_of? A => trueفي المثال أعلاه، Classتُستخدم الفئة كما تُستخدم أي فئة أخرى في روبي. يتم إنشاء فئتين، Aالأولى Bهي فئة أساسية للثانية، ثم يتم فحص نسخة واحدة من كل فئة. تعطي العبارة الأخيرة القيمة "صحيح" لأن الفئة Aالأساسية هي الفئة الأساسية للفئة الأولى b.
علاوة على ذلك، يمكنك طلب فئة أي كائن مباشرةً، و"مقارنتها" (يفترض الكود أدناه تنفيذ الكود أعلاه):
irb(main):008:0> A . instance_of? Class => true irb(main):009:0> a . class => A irb(main):010:0> a . class . class => Class irb(main):011:0> A > B => true irb(main):012:0> B <= A => trueأكشن سكريبت
في لغة ActionScript (as3)، يمكن استخدام الدالة flash.utils.getQualifiedClassNameلاسترداد اسم الفئة/النوع لكائن عشوائي.
// يجب استيراد جميع الفئات المستخدمة في as3 بشكل صريح . استيراد flash.utils.getQualifiedClassName ؛ استيراد flash.display.Sprite ؛// دالة trace تُشبه System.out.println() في جافا أو echo في PHP. trace ( getQualifiedClassName ( "I'm a String" )); // "String" trace ( getQualifiedClassName ( 1 )); // "int"، راجع التحويل الديناميكي لمعرفة السبب. Number trace ( getQualifiedClassName ( new Sprite ())); // "flash.display.Sprite"بدلاً من ذلك، يمكن استخدام عامل التشغيل isلتحديد ما إذا كان الكائن من نوع معين:
// دالة trace تُشبه System.out.println() في جافا أو echo في PHP. trace ( "I'm a String" is String ); // صحيح. trace (" 1 is String "); // خطأ. trace ( "I'm a String" is Number ); // خطأ. trace (" 1 is Number "); // صحيح.يمكن استخدام هذه الوظيفة الثانية لاختبار أصول وراثة الفئات أيضًا:
استيراد flash.display.DisplayObject ؛ استيراد flash.display.Sprite ؛ // يمتد من DisplayObjecttrace ( new Sprite () is Sprite ); // صحيح trace ( new Sprite () is DisplayObject ); // صحيح، لأن Sprite يمتد من DisplayObject trace ( new Sprite () is String ); // خطأاستبطان النوع الميتا
مثل لغة بيرل، يمكن لـ ActionScript أن تتجاوز مجرد الحصول على اسم الفئة، ولكن يمكنها أيضًا الحصول على جميع البيانات الوصفية والوظائف والعناصر الأخرى التي تشكل كائنًا باستخدام flash.utils.describeTypeالوظيفة؛ ويتم استخدام هذا عند تنفيذ الانعكاس في ActionScript.
استيراد flash.utils.describeType ؛ استيراد flash.utils.getDefinitionByName ؛ استيراد flash.utils.getQualifiedClassName ؛ استيراد flash.display.Sprite ؛var className : String = getQualifiedClassName ( new Sprite ()); // "flash.display.Sprite" var classRef : Class = getDefinitionByName ( className ); // مرجع الفئة إلى flash.display{{ليس خطأً مطبعياً|.}}Sprite // على سبيل المثال، 'new classRef()' هو نفسه 'new flash.display.Sprite()' trace ( describeType ( classRef )); // إرجاع كائن XML يصف النوع // هو نفسه : trace(describeType(flash.display.Sprite));انظر أيضاً
مراجع
- ↑ مواصفات لغة جافا: instanceof
- ↑ واجهة برمجة تطبيقات جافا: java.lang.Class
- ↑ وثائق واجهة برمجة تطبيقات Moose meta
- ↑ Class::MOP - بروتوكول الكائنات الوصفية للغة بيرل
روابط خارجية
- تأملات في برنامج روزيتا كود
- البرمجة الكائنية التوجه
- مقارنات لغات البرمجة
