В вычислениях, тип интроспекция является способность программы для изучения на типе или свойства объекта на время выполнения. Некоторые языки программирования обладают такой возможностью.
Самоанализ не следует путать с отражением, которое идет дальше и представляет собой способность программы манипулировать значениями, метаданными, свойствами и функциями объекта во время выполнения. Некоторые языки программирования также обладают такой возможностью (например, Java, Python, Julia и Go ).
Самоанализ типов - ключевая особенность Ruby. В Ruby, класс объекта (предком каждого класса) обеспечивает Object#instance_of?
и Object#kind_of?
методы проверки класса экземпляра. Последний возвращает истину, когда конкретный экземпляр, которому было отправлено сообщение, является экземпляром потомка рассматриваемого класса. Например, рассмотрим следующий пример кода (вы можете сразу же попробовать это с помощью Interactive Ruby Shell ):
$ irb irb(main):001:0gt; A=Class.new =gt; A irb(main):002:0gt; B=Class.new A =gt; B irb(main):003:0gt; a=A.new =gt; #lt;A:0x2e44b78gt; irb(main):004:0gt; b=B.new =gt; #lt;B:0x2e431b0gt; irb(main):005:0gt; a.instance_of? A =gt; true irb(main):006:0gt; b.instance_of? A =gt; false irb(main):007:0gt; b.kind_of? A =gt; true
В приведенном выше примере этот Class
класс используется как любой другой класс в Ruby. Создаются два класса, A
и B
первый является суперклассом второго, затем проверяется по одному экземпляру каждого класса. Последнее выражение дает истину, потому что A
является суперклассом класса b
.
Кроме того, вы можете напрямую запросить класс любого объекта и «сравнить» их (код ниже предполагает выполнение кода выше):
irb(main):008:0gt; A.instance_of? Class =gt; true irb(main):009:0gt; a.class =gt; A irb(main):010:0gt; a.class.class =gt; Class irb(main):011:0gt; A gt; B =gt; true irb(main):012:0gt; B lt;= A =gt; true
В Objective-C, например, и общий Object, и NSObject (в Cocoa / OpenStep ) предоставляют метод, isMemberOfClass:
который возвращает истину, если аргумент метода является экземпляром указанного класса. Метод isKindOfClass:
аналогичным образом возвращает истину, если аргумент наследуется от указанного класса.
Например, предположим, что у нас есть Apple
и Orange
класс, наследующий от Fruit
.
Теперь в eat
методе мы можем написать
- (void)eat:(id)sth { if ([sth isKindOfClass:[Fruit class]]) { // we're actually eating a Fruit, so continue if ([sth isMemberOfClass:[Apple class]]) { eatApple(sth); } else if ([sth isMemberOfClass:[Orange class]]) { eatOrange(sth); } else { error(); } } else { error(); } }
Теперь, когда eat
вызывается с универсальным объектом (an id
), функция будет вести себя правильно в зависимости от типа универсального объекта.
C ++ поддерживает самоанализ типов с помощью ключевых слов typeid и dynamic_cast информации о типах времени выполнения (RTTI). Выражение может быть использовано, чтобы определить, является ли конкретный объект конкретного производного класса. Например: dynamic_cast
Person* p = dynamic_castlt;Person *gt;(obj); if (p != nullptr) { p-gt;walk(); }
typeid
Оператор извлекает std::type_info
объект, описывающий наиболее производный тип объекта:
if (typeid(Person) == typeid(*obj)) { serialize_person( obj); }
Самоанализ типов был частью Object Pascal с момента первоначального выпуска Delphi, который активно использует RTTI для визуального проектирования форм. В Object Pascal все классы происходят от базового класса TObject, который реализует базовые функции RTTI. На имя каждого класса можно ссылаться в коде для целей RTTI; идентификатор имени класса реализован как указатель на метаданные класса, которые можно объявить и использовать как переменную типа TClass. Язык включает оператор is, чтобы определить, является ли объект заданным классом или является его наследником, оператор as, обеспечивающий приведение типов с проверкой типа, и несколько методов TObject. Более глубокий самоанализ (перечисление полей и методов) традиционно поддерживается только для объектов, объявленных в состоянии $ M + (прагма), обычно TPersistent, и только для символов, определенных в опубликованном разделе. Delphi 2010 увеличил это почти до всех символов.
procedure Form1.MyButtonOnClick(Sender: TObject); var aButton: TButton; SenderClass: TClass; begin SenderClass:= Sender.ClassType; //returns Sender's class pointer if sender is TButton then begin aButton:= sender as TButton; EditBox.Text:= aButton.Caption; //Property that the button has but generic objects don't end else begin EditBox.Text:= Sender.ClassName; //returns the name of Sender's class as a string end; end;
Самый простой пример самоанализа типов в Java - это instanceof
оператор. instanceof
Оператор определяет, принадлежит ли конкретный объект к определенному классу (или подкласса этого класса или класса, который реализует этот интерфейс). Например:
if (obj instanceof Person) { Person p = (Person)obj; p.walk(); }
java.lang.Class
Класс является основой более продвинутого самоанализа.
Например, если желательно определить фактический класс объекта (а не то, является ли он членом определенного класса), Object.getClass()
и Class.getName()
можно использовать:
System.out.println(obj.getClass().getName());
В PHP интроспекцию можно выполнить с помощью instanceof
оператора. Например:
if ($obj instanceof Person) { // Do whatever you want }
Самоанализ может быть достигнуто с помощью ref
и isa
функций в Perl.
Мы можем проанализировать следующие классы и их соответствующие экземпляры:
package Animal; sub new { my $class = shift; return bless {}, $class; } package Dog; use base 'Animal'; package main; my $animal = Animal-gt;new(); my $dog = Dog-gt;new();
с использованием:
print "This is an Animal.\n" if ref $animal eq 'Animal'; print "Dog is an Animal.\n" if $dog-gt;isa('Animal');
Гораздо более мощного самоанализа в Perl можно достичь с помощью объектной системы Moose и протокола Class::MOP
метаобъектов ; Например, вы можете проверить, если данный объект делает на роль X:
if ($object-gt;meta-gt;does_role("X")) { # do something... }
Вот как вы можете перечислить полные имена всех методов, которые могут быть вызваны для объекта, вместе с классами, в которых они были определены:
for my $method ($object-gt;meta-gt;get_all_methods) { print $method-gt;fully_qualified_name, "\n"; }
Наиболее распространенный метод интроспекции в Python - использование dir
функции для детализации атрибутов объекта. Например:
class Foo: def __init__(self, val): self.x = val def bar(self): return self.x
gt;gt;gt; dir(Foo(5)) ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'bar', 'x']
Кроме того, встроенные функции type
и isinstance
могут использоваться для определения того, что представляет собой объект, в то время как hasattr
могут определять, что делает объект. Например:
gt;gt;gt; a = Foo(10) gt;gt;gt; b = Bar(11) gt;gt;gt; type(a) lt;type 'Foo'gt; gt;gt;gt; isinstance(a, Foo) True gt;gt;gt; isinstance(a, type(a)) True gt;gt;gt; isinstance(a, type(b)) False gt;gt;gt; hasattr(a, 'bar') True
В ActionScript функцию flash.utils.getQualifiedClassName
можно использовать для получения имени класса / типа произвольного объекта.
// all classes used in as3 must be imported explicitly import flash.utils.getQualifiedClassName; import flash.display.Sprite; // trace is like System.out.println in Java or echo in PHP trace(flash.utils.getQualifiedClassName("I'm a String")); // "String" trace(flash.utils.getQualifiedClassName(1)); // "int", see dynamic casting for why not Number trace(flash.utils.getQualifiedClassName(new flash.display.Sprite())); // "flash.display.Sprite"
В качестве альтернативы is
можно использовать оператор, чтобы определить, принадлежит ли объект определенному типу:
// trace is like System.out.println in Java or echo in PHP trace("I'm a String" is String); // true trace(1 is String); // false trace("I'm a String" is Number); // false trace(1 is Number); // true
Эту вторую функцию также можно использовать для проверки наследования родительских классов :
import flash.display.DisplayObject; import flash.display.Sprite; // extends DisplayObject trace(new flash.display.Sprite() is flash.display.Sprite); // true trace(new flash.display.Sprite() is flash.display.DisplayObject); // true, because Sprite extends DisplayObject trace(new flash.display.Sprite() is String); // false
Как и Perl, ActionScript может пойти дальше, чем получение имени класса, но всех метаданных, функций и других элементов, составляющих объект, использующий flash.utils.describeType
функцию; это используется при реализации отражения в ActionScript.
import flash.utils.describeType; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; import flash.display.Sprite; var className:String = getQualifiedClassName(new flash.display.Sprite()); // "flash.display.Sprite" var classRef:Class = getDefinitionByName(className); // Class reference to flash.display{{Not a typo|.}}Sprite // eg. 'new classRef()' same as 'new flash.display.Sprite()' trace(describeType(classRef)); // return XML object describing type // same as : trace(describeType(flash.display.Sprite));