CodeGym /Các khóa học /C# SELF /Sử dụng reflection

Sử dụng reflection

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

Trong C# bất kỳ class (hoặc struct) nào — là một tập hợp các member. Bao gồm:

  • Fields (fields): biến lưu dữ liệu.
  • Properties (properties): wrapper “thông minh” với get/set.
  • Methods (methods): logic/funcionality.
  • Constructors (constructors): cách tạo instance.
  • Events (events): publish-subscribe thông minh.

Hãy tưởng tượng bạn có một kính lúp ma thuật, với nó bạn có thể nhìn vào bất kỳ type nào, biết được “phần ruột” và thậm chí thay đổi nó. Đó chính là reflection.

Namespace và các type chính của reflection

Trước khi bắt đầu, đừng quên import namespace:

using System.Reflection;

Những class và interface chủ chốt:

Class/interface Mô tả
Type
Cửa vào chính — mọi thứ bắt đầu từ đây
FieldInfo
Thông tin về field
PropertyInfo
Thông tin về property
MethodInfo
Thông tin về method
ConstructorInfo
Thông tin về constructor
EventInfo
Thông tin về event
MemberInfo
“Cha” chung cho tất cả các cái trên

2. Lấy các member của type bằng reflection

Lấy fields và properties

Ví dụ: liệt kê fields và properties

Giả sử ta có class như sau:

public class Person
{
    public string Name { get; set; }
    public int Age;
    private string Secret = "Sekretnoye slovo";
}

Lấy fields:

Type personType = typeof(Person);

FieldInfo[] fields = personType.GetFields(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var field in fields)
{
    Console.WriteLine($"Pole: {field.Name}, Tip: {field.FieldType.Name}, Dostup: {field.Attributes}");
}

Lấy properties:

PropertyInfo[] properties = personType.GetProperties(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var property in properties)
{
    Console.WriteLine($"Svoystvo: {property.Name}, Tip: {property.PropertyType.Name}");
}

Giải thích

  • BindingFlags — là một tập “flag” cho biết tìm member nào (public/private, instance/static, v.v.).
  • Fields và properties khác nhau: field — mẩu dữ liệu, property — method get/set che giấu field.

Lấy methods

MethodInfo[] methods = personType.GetMethods(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (var method in methods)
{
    Console.WriteLine($"Metod: {method.Name}");
}

Bạn sẽ ngạc nhiên có bao nhiêu method được in ra. Sẽ có cả getter/setter của properties, và các method hệ thống từ object!

Bộ lọc hữu ích: để chỉ lấy các method “rõ ràng” của bạn, có thể lọc thêm theo tên hoặc attribute.

Nên tìm gì với các BindingFlags

Tìm gì Ví dụ gọi Mô tả
Fields public
GetFields(BindingFlags.Public | ...)
Tất cả cái nhìn thấy được từ ngoài
Fields private
GetFields(BindingFlags.NonPublic | ...)
Những cái ẩn bên trong
Chỉ instance
BindingFlags.Instance
Chỉ non-static
Chỉ static
BindingFlags.Static
Chỉ static
Tất cả
BindingFlags.Public | NonPublic | Instance | Static
Danh sách đầy đủ

3. Tạo instance type động

Tại sao hay?
Bạn có thể tạo object mà không biết type chính xác ở compile time.

Cách đơn giản — qua Activator.CreateInstance

// Lấy Type bằng một cách nào đó, ví dụ:
Type myType = typeof(Person);
// Tạo object:
object obj = Activator.CreateInstance(myType);
// Bây giờ obj — là instance của Person, nhưng dưới dạng object

Nếu class không có constructor không tham số, có thể truyền tham số:

public class Animal
{
    public string Name { get; }
    public Animal(string name) { Name = name; }
}

// ...
Type animalType = typeof(Animal);
object dog = Activator.CreateInstance(animalType, "Sharik");

Quan trọng!

Console.WriteLine(dog.GetType().Name); // Animal

Cách tùy chỉnh hơn — dùng ConstructorInfo

ConstructorInfo ctor = animalType.GetConstructor(new Type[] { typeof(string) });
object dog = ctor.Invoke(new object[] { "Sharik" });

Trong thực tế thường dùng Activator, nhưng đôi khi cần tham số chính xác hoặc làm việc với constructor private — lúc đó ConstructorInfo hữu dụng.

4. Truy cập fields và properties theo tên

Lấy và thay đổi field

Person p = new Person();
Type t = p.GetType();

FieldInfo field = t.GetField("Age");
field.SetValue(p, 42);
Console.WriteLine(field.GetValue(p)); // 42

Làm việc với field private:

FieldInfo secret = t.GetField("Secret", BindingFlags.NonPublic | BindingFlags.Instance);
if (secret != null)
{
    secret.SetValue(p, "Oy, sekret raskryt");
    Console.WriteLine(secret.GetValue(p));
}
else
{
    Console.WriteLine("Pole Secret ne naydeno");
}

Cẩn thận! Đừng lạm dụng truy cập vào private fields — đó là phá vỡ encapsulation và có thể gây bug khó chịu.

Lấy và thay đổi properties

PropertyInfo pi = t.GetProperty("Name");
pi.SetValue(p, "Vasya");
Console.WriteLine(pi.GetValue(p)); // Vasya

Properties — không phải field, mà là method get/set phía sau màn hình.

Tại sao đôi khi không hoạt động?

Nếu field hoặc property là private, phải thêm flags BindingFlags với giá trị phù hợp (ví dụ NonPublic). Nếu property không có setter, bạn không thể gán giá trị cho nó qua reflection — quy tắc vẫn được giữ.

5. Gọi method theo tên

Gọi method đơn giản không có tham số

public class Greeter
{
    public void SayHello()
    {
        Console.WriteLine("Privet!");
    }
}

Greeter g = new Greeter();
Type t = g.GetType();
MethodInfo mi = t.GetMethod("SayHello");
mi.Invoke(g, null); // Privet!

Gọi method có tham số

public class MathOp
{
    public int Add(int x, int y) => x + y;
}

MathOp calc = new MathOp();
Type t = calc.GetType();
MethodInfo mi = t.GetMethod("Add");
object result = mi.Invoke(calc, new object[] { 7, 5 });
Console.WriteLine(result); // 12

Nếu method là static, truyền null làm đối số đầu tiên cho Invoke.

Làm việc với overloaded methods

MethodInfo mi = t.GetMethod(
    "Add", 
    new Type[] { typeof(int), typeof(int) }
);

Hoặc duyệt và lọc thủ công:

foreach(var method in t.GetMethods()) {
    if (method.Name == "Add" && method.GetParameters().Length == 2) 
    {
        // ... nash metod!
    }
}

6. Những lưu ý hữu ích

Các member, method và property chính để phân tích type

Member của class Method để lấy info Class kết quả Cách đọc/thay đổi giá trị
Field (field)
GetField, GetFields
FieldInfo
.GetValue(obj), .SetValue(obj, x)
Property (prop)
GetProperty, GetProperties
PropertyInfo
.GetValue(obj), .SetValue(obj, x)
Method (method)
GetMethod, GetMethods
MethodInfo
.Invoke(obj, parametry)
Constructor
GetConstructor, GetConstructors
ConstructorInfo
.Invoke(parametry)

Tại sao dùng cái này trong thực tế?

  • Trong ORM frameworks (ví dụ, Entity Framework xây dựng SQL từ models của bạn).
  • Trong hệ thống serialization và parsers (ví dụ, JSON serializers).
  • Trong thư viện generic cho logging, testing, UI generator.
  • Trong phỏng vấn — hay hỏi “làm sao viết universal object copier?”.
  • Để viết plugins, khi chương trình của bạn load và gọi code do người khác viết.

Ngay cả khi bây giờ bạn thấy giống như phép thuật của phim hacker, tin tôi — sẽ hữu ích!

7. Lỗi thường gặp và đặc điểm

Rất nhiều dev nhầm lẫn với BindingFlags — không thấy private fields hoặc ngược lại lấy quá nhiều method. Luôn kiểm tra kết hợp flags: cho private fields dùng NonPublic, cho static dùng Static, v.v.

Khi gọi method qua Invoke truyền arg cùng thứ tự và type như khai báo. Thứ tự/type sai sẽ dẫn tới TargetParameterCountException hoặc ArgumentException. Kết quả trả về là object — đừng quên cast (hoặc dùng pattern matching).

Đừng quên performance: reflection chậm hơn so với gọi trực tiếp. Ở chỗ nóng hãy cache Type/MethodInfo/PropertyInfo hoặc sinh sẵn delegate.

Bạn sẽ thường thấy các method system như get_PropertyNameset_PropertyName. Đó là accessor methods cho properties — không cần tự tạo chúng.

Nếu thay đổi private fields hoặc properties — bạn đang phá vỡ encapsulation. Dùng sai có thể gây lỗi khó tìm. Như chú Ben nói: “Với sức mạnh lớn, trách nhiệm lớn” — dùng cẩn thận, nhất là trong dự án thư viện.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION