Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Xử lý kiểu dữ liệu không xác định trong C#: Từ khóa dynamic

Vấn đề: Strongly Typed Language

C# là ngôn ngữ strongly-typed: mọi biến phải khai báo kiểu tại compile time. Nhưng đôi khi kiểu chỉ biết tại runtime (COM, JSON, scripting, reflection).

Giải pháp: dynamic Keyword

  • Bỏ qua compile-time type checking.
  • Type resolution chuyển sang runtime (DLR - Dynamic Language Runtime).
  • Biến dynamic có thể gán bất kỳ giá trị nào.
  • Tất cả member access (property, method, operator) được resolve tại runtime.

Ví dụ cơ bản


using System;

class Program 
{
    static void Main() 
    {
        dynamic d = 100;
        Console.WriteLine($"Value: {d}, Type: {d.GetType()}"); // Int32
        
        d = "Hello World!!";
        Console.WriteLine($"Value: {d}, Type: {d.GetType()}"); // String
        
        d = true;
        Console.WriteLine($"Value: {d}, Type: {d.GetType()}"); // Boolean
        
        d = DateTime.Now;
        Console.WriteLine($"Value: {d}, Type: {d.GetType()}"); // DateTime
        
        // Gọi method/property không tồn tại → RuntimeBinderException
        try 
        {
            d.NonExistentMethod(); // Runtime error!
        }
        catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex) 
        {
            Console.WriteLine($"Runtime error: {ex.Message}");
        }
    }
}

Kết quả

Value: 100, Type: System.Int32
Value: Hello World!!, Type: System.String
Value: True, Type: System.Boolean
Value: 01/01/2024 10:30:00, Type: System.DateTime
Runtime error: 'System.DateTime' does not contain a definition for 'NonExistentMethod'

Dynamic vs Object vs Var

Đặc điểmdynamicobjectvar
Type resolutionRuntime (DLR)Compile-time (System.Object)Compile-time (inferred)
Member accessBất kỳ member nào (runtime check)Chỉ Object membersChỉ inferred type members
IntelliSenseKhông cóCó (Object members)Có (full)
Compile errorKhông (trừ syntax)CóCó
Runtime errorCó (missing member)InvalidCastException khi castKhông (compile-time catch)
PerformanceChậm (DLR call site caching)NhanhNhanh (giống explicit type)

Use Cases Thực Tế

1. COM Interop (Office, Excel, Word)


// Không cần reference type library, không cần cast liên tục
dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true;
dynamic workbook = excel.Workbooks.Add();
dynamic sheet = workbook.ActiveSheet;
sheet.Cells[1, 1].Value = "Hello Dynamic Excel";
workbook.SaveAs(@"C:\Test.xlsx");
excel.Quit();

2. JSON Parsing Không Cần Model (Quick & Dirty)


using System.Text.Json;

string json = @"{ ""name"": ""John"", ""age"": 30, ""city"": ""Hanoi"" }";

// System.Text.Json - cần model hoặc JsonNode
// dynamic với Newtonsoft.Json (legacy) hoặc JsonElement
dynamic data = JsonSerializer.Deserialize(json); // .NET 6+: JsonNode
// Hoặc dùng Newtonsoft: dynamic data = JsonConvert.DeserializeObject(json);

Console.WriteLine(data.name);  // John
Console.WriteLine(data.age);   // 30

3. Scripting / Plugin System


// Load assembly, tạo instance, gọi method động
Assembly asm = Assembly.LoadFrom("Plugin.dll");
Type type = asm.GetType("Plugin.MyClass");
dynamic instance = Activator.CreateInstance(instance);
instance.Initialize(config); // Gọi method không biết tại compile time
var result = instance.Execute(input);

4. Dynamic XML/HTML Building


dynamic xml = new DynamicXmlBuilder();
xml.Root(
    xml.User(
        xml.Name("John"),
        xml.Age(30)
    )
);
string result = xml.ToString();
// John30

5. ExpandoObject - Dynamic Object Tùy Chỉnh


using System.Dynamic;

dynamic person = new ExpandoObject();
person.Name = "John";
person.Age = 30;
person.SayHello = new Action(() => Console.WriteLine($"Hi, I'm {person.Name}"));

person.SayHello(); // Hi, I'm John

// Thêm property động
person.Address = new { City = "Hanoi", Country = "Vietnam" };

// Duyệt properties (implements IDictionary)
foreach (var prop in (IDictionary)person) 
{
    Console.WriteLine($"{prop.Key}: {prop.Value}");
}

Performance & Caching

  • DLR sử dụng Call Site Caching: lần đầu gọi chậm, các lần sau nhanh (cache delegate).
  • Vẫn chậm hơn static typing ~10-100x tùy scenario.
  • Tránh dùng trong hot path, loop lớn, game loop.

Best Practices

  1. Hạn chế sử dụng - chỉ khi thực sự cần (COM, dynamic JSON, scripting).
  2. Document rõ ràng expected shape của dynamic object.
  3. Try-catch RuntimeBinderException ở boundary.
  4. Ép kiểu về static type sớm nhất có thể: var typed = (ConcreteType)dynamicObj;.
  5. Không dùng cho DTO/Model - dùng record/class + JSON serializer.

C# 11: Required Properties + Dynamic?


// Dynamic không hỗ trợ required properties check tại compile time
// Runtime: vẫn có thể set sau khi tạo
dynamic d = new ExpandoObject();
// d.RequiredProp = "value"; // Không enforce