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ả |
|---|---|
|
Cửa vào chính — mọi thứ bắt đầu từ đây |
|
Thông tin về field |
|
Thông tin về property |
|
Thông tin về method |
|
Thông tin về constructor |
|
Thông tin về event |
|
“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 | |
Tất cả cái nhìn thấy được từ ngoài |
| Fields private | |
Những cái ẩn bên trong |
| Chỉ instance | |
Chỉ non-static |
| Chỉ static | |
Chỉ static |
| Tất cả | |
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) | |
|
|
| Property (prop) | |
|
|
| Method (method) | |
|
|
| Constructor | |
|
|
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_PropertyName và set_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.
GO TO FULL VERSION