Mechanizm refleksji – Wikipedia, wolna encyklopedia

Mechanizm refleksji – w informatyce, proces, dzięki któremu program komputerowy może być modyfikowany w trakcie działania w sposób zależny od własnego kodu oraz od zachowania w trakcie wykonania. Związany z nim paradygmat programowania to programowanie refleksyjne.

Refleksja pozwala zarządzać kodem tak, jakby był danymi. Używa się jej najczęściej do zmieniania standardowego zachowania już zdefiniowanych metod lub funkcji, a także do tworzenia własnych konstrukcji semantycznych modyfikujących język. Z drugiej strony kod wykorzystujący refleksję jest mniej czytelny i nie pozwala na sprawdzenie poprawności składniowej i semantycznej w trakcie kompilacji.

Przykład zastosowania mechanizmu refleksji stanowią języki programowania, w których każdy element kodu źródłowego reprezentowany jest strukturami danych dostępnymi dla programisty. Nazywa się je językami homoikonicznymi, a kod źródłowy ich programów jest w istocie danymi. Od decyzji programisty zależy, które jego fragmenty będą wartościowane, a które traktowane jak wartości stałe niewymagające obliczeń. Proces ten może być wielokierunkowy, np. część kodu źródłowego może zostać potraktowana jak dane, przekształcona, a następnie znów wartościowana – w ten sposób działają tzw. makra składniowe w dialektach języka Lisp.

Mechanizm refleksji jest najczęściej spotykany w językach wysokiego poziomu, szczególnie opartych na maszynie wirtualnej.

Przykłady[edytuj | edytuj kod]

C#[edytuj | edytuj kod]

Poniższy przykład demonstruje użycie refleksji w języku C Sharp używając pakietu System.Reflection

//bez refleksji Foo foo = new Foo(); foo.hello();  //z użyciem refleksji var type = Type.GetType("namespace.Foo"); // string powinien zawierać namespace naszej klasy var foo = Activator.CreateInstance(type); // inicjacja obiektu określonego typu MethodInfo inf = type.GetMethod("hello"); inf.Invoke(foo); // jako drugi parametr metoda Invoke przyjmuje tablicę Object[] są to parametry metody hello. 

Objective-C[edytuj | edytuj kod]

Poniższy przykład demonstruje użycie refleksji w języku Objective-C

// bez refleksji Foo *foo = [[Foo alloc] init]; [foo hello]; [foo release];  // z refleksją id foo = [[NSClassFromString(@"Foo") alloc] init]; SEL selector = NSSelectorFromString(@"hello"); [foo performSelector:selector]; [foo release]; 

Java[edytuj | edytuj kod]

Poniższy przykład w języku Java wykorzystuje pakiet java.lang.reflect.

 // bez refleksji  Foo foo = new Foo();  foo.hello();   // z refleksją  Class cl = Class.forName("Foo");  Method method = cl.getMethod("hello");  method.invoke(cl.newInstance()); 

Oba fragmenty tworzą instancję klasy Foo, następnie wywołują metodę hello() tej klasy. Różnica polega na tym, że w pierwszym fragmencie nazwa klasy i metody są częścią kodu źródłowego, podczas gdy w drugim fragmencie możliwe jest przeniesienie ich do zmiennych, których wartość jest ustalana w czasie wykonania kodu.

Mechanizm refleksji pozwala także na zdobywanie informacji o klasach w trakcie wykonania programu. W poniższym przykładzie Klasa Main sprawdza jaki jest typ zwracany przez metody klasy Bar.

public class Bar {     public String fun(Integer i) {         return "0" + i + ", zglos sie!";     } }  import static java.lang.System.out; import java.lang.reflect.*;  public class Main {     public static void main(String[] args) throws Exception {         String className = "Bar";         Class c = Class.forName(className);         Method[] methodArr = c.getDeclaredMethods();         for (Method m : methodArr) { 	    out.print("Klasa " + className + " ma metode '" + m.getName() + "'"); 	    out.println(" ktora zwraca wartosc typu " + m.getReturnType());         }     } } 

Ruby[edytuj | edytuj kod]

Przykład w języku Ruby, który dodaje metodę klasową once, pozwalającą zaznaczyć, że dana funkcja składowa klasy ma być wykonywana tylko raz. Podprogram modyfikuje kod oznaczonych metod w taki sposób, że nadaje im nową nazwę. Pod starą nazwą umieszcza nową metodę, która buforuje wartość zwracaną przez pierwotnie zdefiniowaną funkcję, tym samym pozwalając się jej wykonać tylko raz.

# part of date.rb - date and time library # Author: Tadayoshi Funaba 1998-2008  class Date  class << self      def once(*ids) # :nodoc: -- restricted       for id in ids         module_eval <<-"end;"           alias_method :__#{id.object_id}__, :#{id.to_s}           private :__#{id.object_id}__           def #{id.to_s}(*args)             @__ca__[#{id.object_id}] ||= __#{id.object_id}__(*args)           end         end;       end     end      private :once    end end 

Inny przykład to rozszerzenie możliwości języka o konstrukcję automatycznie kasującą zawartość wskazanych przez programistę buforów, jeśli uruchomione zostaną wyszczególnione metody. Zadaniem metaprogramu jest tu również opakowanie metod, jednak zapamiętywane są identyfikatory ich obiektów a nie identyfikatory obiektów ich symbolicznych nazw. Metaprogram zawarto w przykładowym module BufferAffects, który można pobrać z serwisu GitHub. Domieszkując ten moduł możemy korzystać z dodatkowych metod klasowych pozwalających na stosowanie w kodzie klauzul buffers_reset_method i attr_affects_buffers:

require 'bufferaffects'  # http://gist.github.com/88178  class Main    # domieszkowanie modułu   extend BufferAffects    # metoda opróżniająca wykorzystywany bufor   buffers_reset_method :reset_path_buffer    # pola które po zmianie powinny wpływać na   # zawartość bufora   attr_affects_buffers :subpart    # standardowe akcesory pól   attr_accessor        :subpart, :otherpart    # metoda opróżniająca bufor   def reset_path_buffer(name)     @path = nil     p "uruchomiono reset dla #{name}"   end    # metoda z buforowanym wyjściem   def path     @path ||= @subpart.to_s + @otherpart.to_s   end  end  # tworzenie nowego obiektu obj = Main.new  # ustawianie jednego z pól # i wyświetlanie buforowanych wynikow obj.subpart = 'test' p obj.path obj.subpart = '1234' p obj.path 

Linki zewnętrzne[edytuj | edytuj kod]