Reflection (khoa học máy tính)

Trong khoa học máy tính, reflection (có thể dịch là "phản tỉnh", "tự xét mình") là việc tiến trình có khả năng xem xét, nội quan,[a] và sửa đổi kết cấu cùng với hành trạng của chính nó.[1]

Bối cảnh lịch sử

[sửa | sửa mã nguồn]

Các máy tính ban sơ nhất đều được lập trình bằng ngôn ngữ bản địa là hợp ngữ, vốn đã có tính reflection, vì ta có thể lập trình trên những kiến trúc gốc này theo cách là định nghĩa lệnh chỉ thị[b] như dữ liệu và viết code tự sửa được chính nó. Khi việc lập trình phần đông đều chuyển sang những ngôn ngữ biên dịch cấp cao hơn như Algol, Cobol, Fortran, Pascal, và C, thì khả năng reflection này phần lớn biến mất, mãi đến thời những ngôn ngữ lập trình mới hơn thì tính năng reflection có sẵn vào hệ thống kiểu dữ liệu mới xuất hiện lại.[cần dẫn nguồn]

Luận án tiến sĩ năm 1982 của Brian Cantwell Smith đã giới thiệu khái niệm computational reflection trong ngôn ngữ lập trình thủ tục và khái niệm về meta-circular interpreter làm thành phần trong 3-Lisp.[2][3]

Công dụng

[sửa | sửa mã nguồn]

Reflection giúp lập trình viên làm nên các thư viện phần mềm tổng loại để hiển thị dữ liệu, xử lý các định dạng dữ liệu khác nhau, thi hành tuần tự hóa hay khử tuần tự dữ liệu cho việc liên lạc, hoặc làm việc bọc[c] và mở bọc dữ liệu cho các container hoặc các burst trong quá trình liên lạc.

Để sử dụng reflection cho hiệu quả thì hầu như luôn luôn cần có kế hoạch nào đó: framework thiết kế, bản mô tả encoding, thư viện đối tượng, bản đồ cơ sở dữ liệu hoặc quan hệ thực thể.

Reflection làm cho ngôn ngữ hợp với mã hướng mạng hơn. Ví dụ, reflection trợ giúp các ngôn ngữ như Java thao tác tốt hơn trong hệ thống mạng bằng cách tạo điều kiện cho các thư viện cho việc tuần tự hóa, bọc gói và biến thiên các định dạng dữ liệu. Các ngôn ngữ không có reflection như C thì cần phải sử dụng trình biên dịch phụ trợ cho tác vụ, như Abstract Syntax Notation, thì mới tạo được mã cho việc tuần tự hóa và bọc gói.

Reflection có thể được dùng để quan sát và sửa đổi việc thực thi chương trình tại runtime. Thành phần trong chương trình hướng reflection có thể giám sát sự thực thi của vùng code và có thể sửa đổi chính nó tùy theo mục tiêu mong muốn của vùng code đó. Điều này thường đạt được bằng cách gán mã chương trình một cách động ngay tại runtime.

Trong các ngôn ngữ lập trình hướng đối tượng như Java, reflection cho phép tra duyệt[d] lớp, giao diện, trường và phương thức trong runtime mà không cần biết tên của lớp, giao diện, trường, phương thức đấy tại compile time. Nó cũng cho phép instantiate đối tượng mới và invoke phương thức.

Reflection thường hay được dùng trong kiểm thử phần mềm, chẳng hạn như để tạo/instantiate mock object trong runtime.

Reflection cũng là sách lược then chốt cho lập trình meta.

Trong một số ngôn ngữ lập trình hướng đối tượng như C#Java, reflection có thể được dùng để lách khỏi các luật về tính khả cập thành viên. Với property của C#, điều này có thể đạt được bằng cách ghi trực tiếp lên trường chống lưng[e] (thường là bị ẩn) của property non-public. Cũng có thể truy xuất phương thức non-public của lớp và kiểu rồi sau đó invoke nó bằng tay. Điều này dùng được cho file nội bộ dự án cùng với những thư viện bên ngoài như assembly của .NET và archive của Java.

Thực hiện

[sửa | sửa mã nguồn]

Ngôn ngữ mà hỗ trợ reflection thì mang lại một số lượng tính năng khả dụng ở runtime, khó mà làm được những tính năng đó nếu dùng ngôn ngữ cấp thấp hơn. Một số tính năng này là:

  • Khám phá và sửa đổi cấu trúc mã nguồn (chẳng hạn như khối mã, lớp, phương thức, giao thức, v.v.) như đối tượng hạng nhất tại runtime.
  • Từ chuỗi khớp với tên symbol của lớp hoặc hàm, đổi sang tham chiếu hoặc invocation tới lớp hoặc hàm đó.
  • Tính giá trị chuỗi như thể nó là mã lệnh trong runtime.
  • Tạo ra trình thông dịch mới cho bytecode của ngôn ngữ để mang lại ý nghĩa hoặc mục đích mới cho cấu trúc lập trình nào đó.

Các tính năng này có thể được thực hiện theo nhiều cách khác nhau. Trong MOO, reflection là một phần tự nhiên của quán ngữ lập trình[f] thường dụng. Khi động từ (phương thức) được gọi, các biến khác nhau như verb (tên của động từ được gọi) và this (đối tượng mà trên đó động từ được gọi) sẽ tự động được điền vào làm ngữ cảnh cho lệnh gọi đấy. Về bảo mật thì thường được quản lý bằng cách dùng lập trình để truy cập call stack: gọi vào hàm callers() sẽ trả về danh sách các phương thức theo thứ tự gọi dần về động từ hiện hành, nên kiểm tra callers()[0] (tức lệnh do người dùng gốc invoke) cho phép động từ bảo vệ chính mình khỏi việc sử dụng trái phép.[4]

Ngôn ngữ biên dịch thì dựa vào hệ thống runtime để cung cấp thông tin về mã nguồn. Ví dụ: file thực thi được biên dịch từ Objective-C thì nó ghi lại tên của tất cả phương thức ở một khối trong file thực thi đó và dành ra một bảng để sắp các tên đó với các phương thức tương ứng (hoặc selector cho các phương thức tương ứng) trong chương trình. Còn ở ngôn ngữ biên dịch mà có hỗ trợ tạo ra hàm ngay ở runtime, chẳng hạn Common Lisp, thì môi trường runtime phải có kèm cả trình biên dịch hoặc trình thông dịch.

Reflection có thể được thực hiện cho các ngôn ngữ không có sẵn reflection bằng cách sử dụng hệ biến đổi chương trình để vạch ra đường lối biến đổi tự động cho mã nguồn.

Cân nhắc bảo mật

[sửa | sửa mã nguồn]

Reflection có thể cho phép người dùng tạo ra dòng điều khiển không ngờ được xuyên qua ứng dụng và có thể lách khỏi các biện pháp bảo mật. Điều này có thể bị những kẻ tấn công khai thác.[5] Các lỗ hổng trong lịch sử ở Java do 'reflection không an toàn' gây ra có thể tạo điều kiện cho code truy xuất đến máy bất khả tín ở xa, từ đó thoát khỏi cơ chế bảo mật sandbox của Java. Một nghiên cứu quy mô lớn về 120 lỗ hổng Java vào năm 2013 đã kết luận rằng 'reflection không an toàn' là lỗ hổng phổ biến nhất trong Java, mặc dù không phải là cái bị khai thác nhiều nhất.[6]

Các đoạn mã sau đây đều tạo ra một instance foo của lớp Foo rồi gọi phương thức PrintHello của nó. Với mỗi ngôn ngữ lập trình, trình tự lệnh để gọi bình thường và trình tự lệnh để gọi dựa trên reflection đều được thể hiện.

Sau đây là ví dụ trong C#:

// Không dùng reflection
Foo foo = new Foo();
foo.PrintHello();

// Dùng reflection
Object foo = Activator.CreateInstance("complete.classpath.and.Foo");
MethodInfo method = foo.GetType().GetMethod("PrintHello");
method.Invoke(foo, null);

Delphi / Object Pascal

[sửa | sửa mã nguồn]

Ví dụ Delphi / Object Pascal sau đây giả định rằng có một lớp TFoo đã được khai báo trong một đơn vị được gọi là Unit1:

uses RTTI, Unit1;

// Không dùng reflection
procedure WithoutReflection;
var
  Foo: TFoo;
begin
  Foo := TFoo.Create;
  try
    Foo.Hello;
  finally
    Foo.Free;
  end;
end;

// Dùng reflection
procedure WithReflection;
var
  RttiContext: TRttiContext;
  RttiType: TRttiInstanceType;
  Foo: TObject;
begin
  RttiType := RttiContext.FindType('Unit1.TFoo') as TRttiInstanceType;
  Foo := RttiType.GetMethod('Create').Invoke(RttiType.MetaclassType, []).AsObject;
  try
    RttiType.GetMethod('Hello').Invoke(Foo, []);
  finally
    Foo.Free;
  end;
end;

Sau đây là ví dụ trong eC:

// Không dùng reflection
Foo foo { };
foo.hello();

// Dùng reflection
Class fooClass = eSystem_FindClass(__thisModule, "Foo");
Instance foo = eInstance_New(fooClass);
Method m = eClass_FindMethod(fooClass, "hello", fooClass.module);
((void (*)())(void *)m.function)(foo);

Sau đây là ví dụ trong Go:

import "reflect"

// Không dùng reflection
f := Foo{}
f.Hello()

// Dùng reflection
fT := reflect.TypeOf(Foo{})
fV := reflect.New(fT)

m := fV.MethodByName("Hello")
if m.IsValid() {
    m.Call(nil)
}

Sau đây là ví dụ trong Java:

import java.lang.reflect.Method;

// Không dùng reflection
Foo foo = new Foo();
foo.hello();

// Dùng reflection
try {
    Object foo = Foo.class.getDeclaredConstructor().newInstance();

    Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
    m.invoke(foo);
} catch (ReflectiveOperationException ignored) {}

JavaScript

[sửa | sửa mã nguồn]

Sau đây là ví dụ trong Javascript:

// Không dùng reflection
const foo = new Foo()
foo.hello()

// Dùng reflection
const foo = Reflect.construct(Foo)
const hello = Reflect.get(foo, 'hello')
Reflect.apply(hello, foo, [])

// Dùng eval
eval('new Foo().hello()')

Sau đây là ví dụ trong Julia:

julia> struct Point
           x::Int
           y
       end

# Tra duyệt bằng reflection
julia> fieldnames(Point)
(:x, :y)

julia> fieldtypes(Point)
(Int64, Any)

julia> p = Point(3,4)

# Truy cập bằng reflection
julia> getfield(p, :x)
3

Objective-C

[sửa | sửa mã nguồn]

Sau đây là ví dụ trong Objective-C, ngầm định rằng code đang sử dụng framework OpenStep hoặc Foundation Kit:

// lớp Foo.
@interface Foo : NSObject
- (void)hello;
@end

// Gửi "hello" tới instance Foo, không dùng reflection.
Foo *obj = [[Foo alloc] init];
[obj hello];

// Gửi "hello" tới instance Foo, có dùng reflection.
id obj = [[NSClassFromString(@"Foo") alloc] init];
[obj performSelector: @selector(hello)];

Sau đây là ví dụ trong Perl:

# Không dùng reflection
my $foo = Foo->new;
$foo->hello;

# hoặc
Foo->new->hello;

# Dùng reflection
my $class = "Foo"
my $constructor = "new";
my $method = "hello";

my $f = $class->$constructor;
$f->$method;

# hoặc
$class->$constructor->$method;

# Dùng eval
eval "new Foo->hello;";

Sau đây là ví dụ trong PHP:

// Không dùng reflection
$foo = new Foo();
$foo->hello();

// Dùng reflection, thông qua Reflections API
$reflector = new ReflectionClass('Foo');
$foo = $reflector->newInstance();
$hello = $reflector->getMethod('hello');
$hello->invoke($foo);

Sau đây là ví dụ trong Python:

# Không dùng reflection
obj = Foo()
obj.hello()

# Dùng reflection
obj = globals()["Foo"]()
getattr(obj, "hello")()

# Dùng eval
eval("Foo().hello()")

Sau đây là ví dụ trong R:

# Không dùng reflection, coi như foo() trả về đối tượng kiểu S3 có mang phương thức "hello"
obj <- foo()
hello(obj)

# Dùng reflection
class_name <- "foo"
generic_having_foo_method <- "hello"
obj <- do.call(class_name, list())
do.call(generic_having_foo_method, alist(obj))

Sau đây là ví dụ sử dụng Ruby:

# Không dùng reflection
obj = Foo.new
obj.hello

# Dùng reflection
class_name = "Foo"
method_name = :hello
obj = Object.const_get(class_name).new
obj.send method_name

# Dùng eval
eval "Foo.new.hello"

Sau đây là ví dụ sử dụng Xojo:

' Không dùng reflection
Dim fooInstance As New Foo
fooInstance.PrintHello

' Dùng reflection
Dim classInfo As Introspection.Typeinfo = GetTypeInfo(Foo)
Dim constructors() As Introspection.ConstructorInfo = classInfo.GetConstructors
Dim fooInstance As Foo = constructors(0).Invoke
Dim methods() As Introspection.MethodInfo = classInfo.GetMethods
For Each m As Introspection.MethodInfo In methods
  If m.Name = "PrintHello" Then
    m.Invoke(fooInstance)
  End If
Next

Ghi chú thuật ngữ

[sửa | sửa mã nguồn]
  1. ^ Introspect
  2. ^ Instruction
  3. ^ Bundling
  4. ^ Inspect
  5. ^ Backing field
  6. ^ Programming idiom

Tham khảo

[sửa | sửa mã nguồn]

Trích dẫn

[sửa | sửa mã nguồn]
  1. ^ A Tutorial on Behavioral Reflection and its Implementation by Jacques Malenfant et al. (PDF), unknown, Bản gốc (PDF) lưu trữ ngày 21 tháng 8 năm 2017, truy cập ngày 23 tháng 6 năm 2019
  2. ^ Brian Cantwell Smith, Procedural Reflection in Programming Languages, Department of Electrical Engineering and Computer Science, Massachusetts Institute of Technology, PhD dissertation, 1982.
  3. ^ Brian C. Smith. Reflection and semantics in a procedural language Lưu trữ 2015-12-13 tại Wayback Machine. Technical Report MIT-LCS-TR-272, Massachusetts Institute of Technology, Cambridge, Massachusetts, January 1982.
  4. ^ “The MOO Programming Language”. Bản gốc lưu trữ ngày 29 tháng 11 năm 2022. Truy cập ngày 21 tháng 7 năm 2022.
  5. ^ Barros, Paulo; Just, René; Millstein, Suzanne; Vines, Paul; Dietl, Werner; d'Amorim, Marcelo; Ernst, Michael D. (tháng 8 năm 2015). Static Analysis of Implicit Control Flow: Resolving Java Reflection and Android Intents (PDF) (Bản báo cáo). University of Washington. UW-CSE-15-08-01. Truy cập ngày 7 tháng 10 năm 2021.
  6. ^ Eauvidoum, Ieu; disk noise (5 tháng 10 năm 2021). “Twenty years of Escaping the Java Sandbox”. Phrack. 10 (46). Truy cập ngày 7 tháng 10 năm 2021.Quản lý CS1: sử dụng tham số tác giả (liên kết)

Đọc thêm

[sửa | sửa mã nguồn]
  • Ira R. Forman và Nate Forman, Java Reflection in Action (2005),ISBN 1-932394-18-4
  • Ira R. Forman và Scott Danforth, Putting Metaclasses to Work (1999), ISBN 0-201-43305-2

Liên kết ngoài

[sửa | sửa mã nguồn]
Chúng tôi bán
Bài viết liên quan
Design Thinking for Data Visualization: A Practical Guide for Data Analysts
Design Thinking for Data Visualization: A Practical Guide for Data Analysts
Tư duy thiết kế (Design Thinking) là một hệ tư tưởng và quy trình giải quyết các vấn đề phức tạp theo cách lấy người dùng cuối (end-user) làm trung tâm
[Review sách] Tàn ngày để lại: Còn lại gì sau một quá khứ huy hoàng đã mất
[Review sách] Tàn ngày để lại: Còn lại gì sau một quá khứ huy hoàng đã mất
Trong cuộc phỏng vấn với bà Sara Danius - thư ký thường trực Viện Hàn lâm Thụy điển, bà nói về giải thưởng Nobel Văn học dành cho Kazuo
Bố cục chụp ảnh là gì?
Bố cục chụp ảnh là gì?
Bố cục chụp ảnh là cách chụp bố trí hợp lí các yếu tố/ đối tượng khác nhau trong một bức ảnh sao cho phù hợp với ý tưởng người chụp.
Phổ hiền Rien: Lãnh đạo Lord Tensen - Jigokuraku
Phổ hiền Rien: Lãnh đạo Lord Tensen - Jigokuraku
Rien (Từ điển, Bính âm: Lián), còn được gọi là biệt danh Fugen Jōtei (Từ điển, Nghĩa đen: Shangdi Samantabhadra), là một Sennin cấp Tensen, người từng là người cai trị thực sự của Kotaku, tổ tiên của Tensens, và là người lãnh đạo của Lord Tensen.