Trong lập trình .NET, Reflection là một khái niệm quan trọng mà mỗi developer cần hiểu để tận dụng hết khả năng của ngôn ngữ lập trình C#.NET. Reflection cung cấp khả năng động hóa trong quá trình thực thi ứng dụng, cho phép bạn xem và tương tác với thông tin metadata của các loại (types) trong thời gian chạy.

Giới thiệu về Reflection trong C#.NET
Trong lập trình hướng đối tượng (OOP), chúng ta định nghĩa các đặc điểm và hành vi của đối tượng bằng cách sử dụng các class, struct,… Dữ liệu mô tả các models, definitions, và các thành phần ứng dụng tương tự thường được biết đến chung là siêu dữ liệu (metadata). Trong C#, chúng ta có thể xử lý siêu dữ liệu này vào thời gian chạy thông qua một cơ chế tích hợp mạnh mẽ được gọi là Reflection.
Ví Dụ Thực Tế
Và còn nhiều hơn thế nữa…
Giới thiệu về C# Type Class
class Example
{
public int MyProperty { get; set; }
public void MyMethod() { }
}
class Program
{
static void Main()
{
// Lấy Type của lớp Example
Type exampleType = typeof(Example);
// Hiển thị tên kiểu
Console.WriteLine($"Type Name: {exampleType.Name}");
// Lấy thông tin về thuộc tính
Console.WriteLine("Properties in Example class:");
foreach (var property in exampleType.GetProperties())
{
Console.WriteLine(property.Name);
}
// Lấy thông tin về phương thức Console.WriteLine("Methods in Example class:");
foreach (var method in exampleType.GetMethods())
{
Console.WriteLine(method.Name);
}
}
}
- typeof(Example) trả về một đối tượng Type đại diện cho lớp Example.
- Chúng ta sử dụng đối tượng Type để truy cập thông tin như tên kiểu (Name).
- Sử dụng các phương thức như GetProperties() và GetMethods() để lấy thông tin về thuộc tính và phương thức của kiểu.
Giới thiệu về System.Reflection
System.Reflection là một namespace trong .NET và nó chứa các class và interface. Những thành phần này cung cấp cách để có thể quản lý các trường (fields), phương thức (methods), và kiểu dữ liệu cũng như tùy chọn để chúng ta có thể gọi các kiểu đó một cách động. Một số class thường được sử dụng nhất là:
1. Assembly
class Program
{
static void Main()
{
// Đường dẫn đến file assembly (cần thay thế bằng đường dẫn thực tế của bạn)
string assemblyPath = "C:\Path\To\Your\Assembly.dll";
try
{
// Tải assembly từ đường dẫn
Assembly loadedAssembly = Assembly.LoadFrom(assemblyPath);
// Hiển thị tên của assembly
Console.WriteLine($"Assembly Name: {loadedAssembly.FullName}");
// Lấy danh sách các kiểu trong assembly
Console.WriteLine("Types in the Assembly:");
foreach (Type type in loadedAssembly.GetTypes())
{
Console.WriteLine(type.FullName);
}
// Lấy thông tin về phiên bản của assembly
Version version = loadedAssembly.GetName().Version;
Console.WriteLine($"Assembly Version: {version}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
- Chúng ta sử dụng phương thức Assembly.LoadFrom() để tải một assembly từ đường dẫn cụ thể. Bạn cần thay thế giá trị của assemblyPath bằng đường dẫn thực tế đến file assembly của bạn.
- Sau đó, chúng ta sử dụng loadedAssembly.FullName để lấy tên đầy đủ của assembly và loadedAssembly.GetTypes() để lấy danh sách các kiểu trong assembly.
- Cuối cùng, chúng ta sử dụng loadedAssembly.GetName().Version để lấy thông tin về phiên bản của assembly.
2.Module:
// Load assembly từ tệp tin DLL
Assembly assembly = Assembly.LoadFrom("Path\To\Your\Assembly.dll");
// Lấy tất cả các Modules trong Assembly
Module[] modules = assembly.GetModules();
// Duyệt qua từng Module và hiển thị thông tin
foreach (Module module in modules)
{
Console.WriteLine($"Module Name: {module.Name}");
// Lấy tất cả các kiểu trong Module
Type[] types = module.GetTypes();
Console.WriteLine("Types in the Module:");
foreach (Type type in types)
{
Console.WriteLine(type.FullName);
}
Console.WriteLine();
}
3. MethodInfo:
public class Example
{
public void DisplayMessage(string message)
{
Console.WriteLine($"Message: {message}");
}
}
class Program
{
static void Main()
{
// Tạo một đối tượng Example
Example example = new Example();
// Lấy kiểu của đối tượng
Type type = example.GetType();
// Lấy MethodInfo cho phương thức DisplayMessage
MethodInfo methodInfo = type.GetMethod("DisplayMessage");
// Kiểm tra xem phương thức có tồn tại hay không
if (methodInfo != null)
{
// Gọi phương thức và truyền tham số
object[] parameters = { "Hello, Reflection!" };
methodInfo.Invoke(example, parameters);
}
}
}
- Tạo một đối tượng Example.
- Lấy Type của đối tượng Example.
- Sử dụng GetMethod để lấy MethodInfo cho phương thức “DisplayMessage”.
- Kiểm tra xem phương thức có tồn tại hay không.
- Sử dụng Invoke để gọi thực thi phương thức và truyền tham số.
Output của chương trình sẽ là:Message: Hello, Reflection!
4. PropertyInfo:
public class Example
{
public string MyProperty { get; set; }
}
class Program
{
static void Main()
{
// Tạo một đối tượng Example
Example example = new Example();
// Lấy kiểu của đối tượng
Type type = example.GetType();
// Lấy PropertyInfo cho thuộc tính MyProperty
PropertyInfo propertyInfo = type.GetProperty("MyProperty");
// Kiểm tra xem thuộc tính có tồn tại hay không
if (propertyInfo != null)
{
// Gán giá trị mới cho thuộc tính
propertyInfo.SetValue(example, "New Value");
// Đọc giá trị của thuộc tính
string value = (string)propertyInfo.GetValue(example);
Console.WriteLine($"MyProperty value: {value}");
}
}
}
5. FieldInfo:
public class Example
{
public string MyField;
}
class Program
{
static void Main()
{
// Tạo một đối tượng Example
Example example = new Example();
// Lấy kiểu của đối tượng
Type type = example.GetType();
// Lấy FieldInfo cho trường MyField
FieldInfo fieldInfo = type.GetField("MyField");
// Kiểm tra xem trường có tồn tại hay không
if (fieldInfo != null)
{
// Gán giá trị mới cho trường
fieldInfo.SetValue(example, "New Value");
// Đọc giá trị của trường
string value = (string)fieldInfo.GetValue(example);
Console.WriteLine($"MyField value: {value}");
}
}
}
6. EventInfo:
public class Example
{
public event EventHandler MyEvent;
public void RaiseEvent()
{
MyEvent?.Invoke(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
// Tạo một đối tượng Example
Example example = new Example();
// Lấy kiểu của đối tượng
Type type = example.GetType();
// Lấy EventInfo cho sự kiện MyEvent
EventInfo eventInfo = type.GetEvent("MyEvent");
// Kiểm tra xem sự kiện có tồn tại hay không
if (eventInfo != null)
{
// Thêm một phương thức xử lý sự kiện
EventHandler handler = (sender, args) =>
{
Console.WriteLine("Event handled!");
};
eventInfo.AddEventHandler(example, handler);
// Kích hoạt sự kiện
example.RaiseEvent();
// Xóa phương thức xử lý sự kiện
eventInfo.RemoveEventHandler(example, handler);
}
}
}
7. ConstructorInfo:
public class Example
{
public Example(int value)
{
Console.WriteLine($"Constructor called with value: {value}");
}
}
class Program
{
static void Main()
{
// Lấy kiểu của đối tượng Example
Type type = typeof(Example);
// Lấy ConstructorInfo cho constructor có tham số int
ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(int) });
// Kiểm tra xem constructor có tồn tại hay không
if (constructorInfo != null)
{
// Tạo một đối tượng mới từ constructor
object[] parameters = { 42 };
Example example = (Example)constructorInfo.Invoke(parameters);
}
}
}
- Chúng ta lấy kiểu của đối tượng Example bằng cách sử dụng typeof.
- Sử dụng GetConstructor để lấy ConstructorInfo cho constructor có tham số là int.
- Kiểm tra xem constructor có tồn tại hay không.
- Sử dụng Invoke để tạo một đối tượng mới từ constructor và truyền giá trị 42 cho tham số.
Output của chương trình sẽ là:Constructor called with value: 42
8. CustomAttributeData:
Thực chiến với C# Reflection
1. Inspecting Class Properties
public class ExampleClass
{
public string Name { get; set; }
public int Age { get; set; }
}
// Get Type of ExampleClass
Type classType = typeof(ExampleClass);
// Iterate through properties and get names & types
foreach (PropertyInfo property in classType.GetProperties())
{
Console.WriteLine($"{property.Name} ({property.PropertyType.Name})");
}
Như ví dụ trên, chúng ta có một hàm đơn giản ExampleClass với hai thuộc tính: Name và Age. Chúng ta lấy Type, sau đó duyệt qua các thuộc tính của nó, hiển thị tên thuộc tính và loại thuộc tính.
2. Invoking Methods Dynamically
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
// Get Type of Calculator class
Type calculatorType = typeof(Calculator);
// Create an instance of Calculator
object calculatorInstance = Activator.CreateInstance(calculatorType);
// Get the Add method
MethodInfo addMethod = calculatorType.GetMethod("Add");
// Invoke the Add method dynamically
object result = addMethod.Invoke(calculatorInstance, new object[] { 10, 20 });
Console.WriteLine($"10 + 20 = {result}"); // 10 + 20 = 30
Trong ví dụ này, chúng ta có một class Calculator với một phương thức Add. Chúng ta tạo một instance của Calculator bằng cách sử dụng Activator.CreateInstance(). Sau đó, chúng ta nhận được phương thức Add bằng cách sử dụng GetMethod(“Add”), và cuối cùng, chúng ta gọi phương thức Add đó một cách linh hoạt, truyền các tham số cần thiết và hiển thị kết quả trên console.
3. Add Property to Dynamic Object
using System.Dynamic;
// Create a dynamic object
dynamic expando = new ExpandoObject();
// Add properties using dynamic keyword
expando.FirstName = "John";
expando.LastName = "Doe";
// Use PropertyInfo to add properties by reflection
Type expandoType = expando.GetType();
PropertyInfo ageProperty = expandoType.GetProperty("Age");
if (ageProperty == null)
{
IDictionary<string, object> expandoDict = (IDictionary<string, object>)expando;
expandoDict["Age"] = 30;
}
Console.WriteLine($"{expando.FirstName} {expando.LastName}, {expando.Age} years old");
4. Custom Attributes and Metadata
// Custom attribute definition
public class MyCustomAttribute : Attribute
{
public string Description { get; }
public MyCustomAttribute(string description)
{
Description = description;
}
}
[MyCustomAttribute("This is a custom attribute applied to a class.")]
public class ExampleClass
{
// ...
}
// Get the custom attribute from ExampleClass
Type exampleType = typeof(ExampleClass);
object[] attributes = exampleType.GetCustomAttributes(typeof(MyCustomAttribute), false);
if (attributes.Length > 0)
{
MyCustomAttribute customAttribute = (MyCustomAttribute)attributes[0];
Console.WriteLine($"Custom Attribute: {customAttribute.Description}");
}
Trong ví dụ trên, chúng ta tạo một class MyCustomAttribute kế thừa từ Attribute. Sau đó, apply custom attribute này cho ExampleClass và truy vấn sau đó bằng cách sử dụng GetCustomAttributes(). Sau khi được truy xuất, chuyển thuộc tính thành loại cụ thể và truy cập các thuộc tính của nó.
5. Generate Code
public class Customer
{
public string FirstName { get; set; }
public int BirthYear { get; set; }
}
public static void GenerateObjectInitializer(Type targetType)
{
StringBuilder builder = new StringBuilder($"new {targetType.Name}n{{n");
PropertyInfo[] properties = targetType.GetProperties();
foreach (PropertyInfo property in properties)
{
builder.AppendLine($"{property.Name} = default({property.PropertyType.Name}),");
}
builder.AppendLine("};");
string generatedCode = builder.ToString();
Console.WriteLine(generatedCode);
}
// Call the GenerateObjectInitializer method
GenerateObjectInitializer(typeof(Customer));
Output:new Customer{FirstName = default(String),BirthYear = default(Int32),};
Phương thức GenerateObjectInitializer() lấy một tham số Type và tạo ra đoạn code khởi tạo đối tượng tương ứng cho loại đó. Sau đó, chúng ta gọi phương thức này, truyền typeof(Customer) vào và xem code được tạo được in trên console.
6. Dynamic Method Dispatching
using System.Dynamic;
dynamic dynamicInstance = new ExpandoObject();
// Add properties to dynamic instance
dynamicInstance.FirstName = "John";
dynamicInstance.LastName = "Doe";
// Get FullName dynamically
Func<dynamic, string> getFullName = d => $"{d.FirstName} {d.LastName}";
Console.WriteLine(getFullName(dynamicInstance)); // John Doe
Lời kết
Mong bài viết hữu ích với bạn.Hieu Ho.