Reflection trong C#

Reflection được sử dụng để lấy thông tin kiểu dữ liệu trong thời gian chạy. Các lớp cung cấp quyền truy cập vào siêu dữ liệu của một chương trình đang chạy nằm trong namespace System.Reflection .

Namespace System.Reflection chứa các lớp cho phép bạn lấy thông tin về ứng dụng và tự động thêm các kiểu, giá trị và đối tượng vào ứng dụng.

Ứng dụng của Reflection

Reflection có các ứng dụng sau:

  • Nó cho phép xem thông tin thuộc tính trong thời gian chạy.
  • Nó cho phép kiểm tra các kiểu dữ liệu khác nhau trong một assembly và khởi tạo thể hiện cho các kiểu dữ liệu này.
  • Nó cho phép liên kết muộn với các phương thức và thuộc tính.
  • Nó cho phép tạo các kiểu mới trong thời gian chạy và sau đó thực hiện một số tác vụ bằng cách sử dụng các kiểu đó.

Đọc siêu dữ liệu

Chúng tôi đã đề cập trong hướng dẫn trước rằng bạn có thể đọc thông tin thuộc tính bằng cách sử dụng Reflection. Nếu bạn bỏ lỡ bài viết này thì có thể xem tại đây:

Attribute trong C# | Comdy
Attribute trong C# là gì? Có những loại attribute nào trong C#? Làm sao để sử dụng attribute trong C#.

Các đối tượng MemberInfo của lớp System.Reflection cần được khởi tạo để khám phá các thuộc tính liên kết với một lớp. Để làm điều này, bạn khai báo như sau:

System.Reflection.MemberInfo info = typeof(MyClass);

Chương trình sau đây minh họa điều này:

using System;

[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute : System.Attribute 
{
   private string _topic;
   public readonly string Url;
   
   public string Topic   // Topic is a named parameter 
   {
      get { return _topic; }
      set { _topic = value; }
   }
   
   public HelpAttribute(string url)   // url is a positional parameter 
   {
      this.Url = url;
   }
}

[HelpAttribute("Information on the class MyClass")]
class MyClass 
{

}

namespace AttributeAppl 
{
   class Program 
   {
      static void Main(string[] args) 
      {
         System.Reflection.MemberInfo info = typeof(MyClass);
         object[] attributes = info.GetCustomAttributes(true);
         
         for (int i = 0; i < attributes.Length; i++) 
         {
            System.Console.WriteLine(attributes[i]);
         }
         Console.ReadKey();
      }
   }
}

Khi được biên dịch và chạy, nó sẽ hiển thị tên của các attribute tùy chỉnh được sử dụng trong lớp MyClass:

HelpAttribute

Ví dụ sử dụng Reflection trong C#

Trong ví dụ này, chúng tôi sử dụng attribute DebugInfoAttribute được tạo trong chương trước và sử dụng Reflection để đọc siêu dữ liệu trong lớp Rectangle.

using System;
using System.Reflection;

namespace BugFixApplication 
{
   //a custom attribute BugFix to be assigned to a class and its members
   [AttributeUsage(
      AttributeTargets.Class |
      AttributeTargets.Constructor |
      AttributeTargets.Field |
      AttributeTargets.Method |
      AttributeTargets.Property,
      AllowMultiple = true)]
   public class DebugInfoAttribute : System.Attribute 
   {
      private int bugNo;
      private string developer;
      private string lastReview;
      public string message;
      
      public DebugInfoAttribute(int bg, string dev, string d) 
      {
         this.bugNo = bg;
         this.developer = dev;
         this.lastReview = d;
      }
      public int BugNo 
      {
         get { return bugNo; }
      }
      public string Developer 
      {
         get { return developer; }
      }
      public string LastReview 
      {
         get { return lastReview; }
      }
      public string Message 
      {
         get { return message; }
         set { message = value; }
      }
   }
   
   [DebugInfo(45, "Zara Ali", "12/8/2019", Message = "Return type mismatch")]
   [DebugInfo(49, "Nuha Ali", "10/10/2019", Message = "Unused variable")]   
   class Rectangle 
   {
      //member variables
      protected double length;
      protected double width;
      
      public Rectangle(double l, double w) 
      {
         length = l;
         width = w;
      }
      
      [DebugInfo(55, "Zara Ali", "19/10/2019", Message = "Return type mismatch")]
      public double GetArea() 
      {
         return length * width;
      }
      
      [DebugInfo(56, "Zara Ali", "19/10/2019")]
      public void Display() 
      {
         Console.WriteLine("Length: {0}", length);
         Console.WriteLine("Width: {0}", width);
         Console.WriteLine("Area: {0}", GetArea());
      }
   }//end class Rectangle
   
   class ExecuteRectangle 
   {
      static void Main(string[] args) 
      {
         Rectangle r = new Rectangle(4.5, 7.5);
         r.Display();
         Type type = typeof(Rectangle);
         
         //iterating through the attribtues of the Rectangle class
         foreach (Object attr in type.GetCustomAttributes(false)) 
         {
            DebugInfoAttribute dbi = attr as DebugInfoAttribute;            
            if (dbi != null) 
            {
               Console.WriteLine("Bug no: {0}", dbi.BugNo);
               Console.WriteLine("Developer: {0}", dbi.Developer);
               Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
               Console.WriteLine("Remarks: {0}", dbi.Message);
            }
         }
         
         //iterating through the method attribtues
         foreach (MethodInfo m in type.GetMethods()) 
         {            
            foreach (Attribute a in m.GetCustomAttributes(true)) 
            {
               DebugInfoAttribute dbi = a as DebugInfoAttribute;               
               if (null != dbi) 
               {
                  Console.WriteLine("Bug no: {0}, for Method: {1}", dbi.BugNo, m.Name);
                  Console.WriteLine("Developer: {0}", dbi.Developer);
                  Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
                  Console.WriteLine("Remarks: {0}", dbi.Message);
               }
            }
         }
         Console.ReadLine();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

Length: 4.5
Width: 7.5
Area: 33.75
Bug No: 49
Developer: Nuha Ali
Last Reviewed: 10/10/2019
Remarks: Unused variable
Bug No: 45
Developer: Zara Ali
Last Reviewed: 12/8/2019
Remarks: Return type mismatch
Bug No: 55, for Method: GetArea
Developer: Zara Ali
Last Reviewed: 19/10/2019
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Zara Ali
Last Reviewed: 19/10/2019
Remarks: 

Cách Reflection làm việc trong C#

Lớp chính để Reflection làm việc là lớp System.Type. Đây là lớp trừu tượng đại diện cho một kiểu dữ liệu trong Hệ thống kiểu chung (CTS). Khi bạn sử dụng lớp này, bạn có thể tìm thấy các kiểu dữ liệu được sử dụng trong một mô-đun và namespace.

Nó cũng xác định xem một kiểu dữ liệu cụ thể là kiểu tham chiếu hay kiểu giá trị. Bạn có thể phân tích các siêu dữ liệu tương ứng để xem qua các thành phần này:

  • Trường.
  • Thuộc tính.
  • Phương thức.
  • Sự kiện.

Các ràng buộc muộn (late bindings) cũng có thể thực hiện được bằng cách sử dụng Reflection.

Hãy xem ví dụ sau, bạn có nhiều assembly để thực hiện một số tác vụ khác nhau và không biết chính xác assembly nào sẽ được nạp trong thời gian biên dịch. Trong trường hợp này, bạn có thể yêu cầu người dùng chọn assembly trong thời gian chạy để ứng dụng có thể nạp assembly đó.

Bạn có thể làm điều đó bằng cách sử dụng một trong các phương thức sau của lớp System.Reflection.Assembly:

  • LoadFrom
  • LoadWithPartialName

Các lớp tiện ích của Reflection trong C#

Có một số lớp tiện ích của Reflection trong C# như sau:

  1. Sử dụng lớp Module để lấy tất cả thông tin của các phương thức được định nghĩa trong mô-đun.
  2. Sử dụng lớp MethodInfo để lấy thông tin như tham số, tên, kiểu trả về, chỉ thị truy cập và chi tiết triển khai của phương thức.
  3. Sử dụng lớp EventInfo để xem kiểu dữ liệu của trình xử lý sự kiện, tên, kiểu khai báo và thuộc tính tùy chỉnh.
  4. Sử dụng lớp ConstructorInfo để lấy dữ liệu về các tham số, chỉ thị truy cập và chi tiết triển khai của phương thức khởi tạo.
  5. Sử dụng lớp Assembly để tải các mô-đun được liệt kê trong một assembly được chỉ định.
  6. Sử dụng lớp PropertyInfo để lấy dữ liệu về kiểu khai báo, kiểu dữ liệu, tên và trạng thái có thể ghi của thuộc tính hoặc để trả về và gán giá trị cho thuộc tính.
  7. Sử dụng lớp CustomAttributeData để tìm hiểu thông tin về các attribute tùy chỉnh hoặc để xem lại các attribute mà không phải tạo thể hiện mới của attribute.


Bài viết liên quan:

Hướng dẫn này sẽ giúp bạn tìm hiểu về đọc ghi file (File I/O) trong C# và sử dụng các lớp tiện ích để đọc ghi file.

Attribute trong C#

  • 7 min read

Attribute trong C# là gì? Có những loại attribute nào trong C#? Làm sao để sử dụng attribute trong C#.

Các tính năng mới của C# 8.0 được Microsoft phát hành cùng với .NET Framework 4.8 và Visual Studio 2019.