DLR – Dynamic Programming: DLR và dynamic keyword trong C#

Phiên bản C# 4.0 ra đời cung cấp một kĩ thuật lập trình mới mà nhiều ngôn ngữ lập trình khác (như PHP, javascript, Python, Ruby,…) đã sử dụng là Dynamic programming, thêm là một từ khóa mới: “dynamic”. Có thể thấy, .Net không ngừng phát triển để thêm vào những khái niệm và kĩ thuật lập trình mới cho các ngôn ngữ của mình. Vậy Dynamic programming là gì và từ khóa dynamic sử dụng ra sao?

Dynamic Programming và Dynamic Language Runtime (DLR)

Có thể nói Dynamic programming là đặc điểm chính mà phiên C# 4.0 giới thiệu, đi kèm với một môi trường thực thi mới của .Net là Dynamic Language Runtime (DLR). DLR có tác dụng cung cấp thêm những dịch vụ vào Common Language Runrim (CLR) để hỗ trợ tính năng Dynamic programming cho các ngôn ngữ lập trình.

Có thể nói DLR là bắt nguồn từ dự án phát triển ngôn ngữ IronPython, ngôn ngữ hỗ trợ dynamic programming đầu tiên trong .Net. Và từ đó, DLR được tạo ra và trở thành môi trường trung gian để hỗ trợ dynamic programming cho các ngôn ngữ lập trình .Net, như mô hình sau:

DLR architecture

(Nguồn ảnh: http://msdn.microsoft.com/en-us/library/dd233052.aspx)

Những lợi ích mà DLR đem lại:

–          Hạn chế những công đoạn cần thiết để tạo ra một ngôn ngữ lập trình như kiểm tra, phân tích cú pháp, ngữ nghĩa. Sẽ rất hữu dụng nếu như bạn áp dụng để tạo ra một ngôn ngữ lập trình dạng dynamic programming trên nền .Net.

–           Hỗ trợ Dynamic programming cho những ngôn ngữ lập trình sử dụng kiểu dữ liệu tĩnh (Statically Typed Language hay Strongly typed) như Java, C++, C#,…

–          Các thư viện và đối tượng có thể được chia sẻ và sử dụng trong các ngôn ngữ lập trình khác nhau, giữa những ngôn ngữ statically typed và dynamic typed.

–          Rút ngắn mã lệnh và truy xuất nhanh những thuộc tính, thành phần của đối tượng.

Để tìm hiểu thêm về DLR, bạn có thể ghé thăm và xem DLR Document tại trang CodePlex, bài viết giới thiệu về Dynamic Language Runtime trên MSDN.

 ‘dynamic’ keyword

Từ khóa ‘dynamic’ được sử dụng như kiểu dữ liệu để khai báo trong C# 4.0. Các đối tượng được khai báo với ‘dynamic’ sẽ không xác định được kiểu cho đến khi chương trình được thực thi.Tức là khi biên dịch, compiler sẽ bỏ qua phần kiểm tra kiểu (như phần trên đã giới thiệu), tính năng Intellisense trong lúc soạn thảo cũng sẽ không được hỗ trợ  khi bạn sử dụng các đối tượng có kiểu ‘dynamic’.

Với từ khóa này, bạn không cần thực hiện kiểm tra, chuyển đổi, ép kiểu hay dùng reflection để kiểm tra hoặc truy xuất đến một thành phần của đối tượng. Học cách sử dụng từ khóa này rất đơn giản, không cần tìm kiếm những ví dụ để bắt chước, hãy thử bất kì nơi nào bạn muốn và kiểm tra kết quả.

Ví dụ như bạn muốn lấy giá trị của property Length của các đối tượng trả về từ phương thức GetSomeThings(), giả sử mọi đối tượng đều property này.

static System.Collections.Generic.IEnumerable<object> GetSomeThings()
{
    yield return "a string";
    yield return new System.Collections.BitArray(6);
    yield return new int[] { 1,2,3,4};
}

Sử dụng theo cách thông thường với reflection:

foreach (object obj in GetSomeThings())
{
    System.Reflection.PropertyInfo prop = obj.GetType().GetProperty("Length");

    int number = (int)prop.GetValue(obj, null);
    Console.WriteLine(number);
}

Với dynamic programming:

foreach (dynamic obj in GetSomeThings())
{
    Console.WriteLine(obj.Length);
}

Cả hai ví dụ đều xuất ra cùng kết quả:

8
6
4

Một ví dụ khác, bạn không cần ép kiểu Control sang TextBox hay ListBox, đoạn mã chạy mà không gặp bất kì lỗi nào:

dynamic ctl = this.Controls["textbox1"];
ctl.Multiline=true;

ctl=this.Controls["listbox1"];
ctl.Items.Add("Test");

Các biến khai báo với dynamic có thể nhận một giá trị với kiểu dữ liệu mới:

dynamic a=100;
Console.WriteLine(a.GetType());
a="text";
Console.WriteLine(a.GetType());

Output:
System.Int32
System.String

Bạn có thể sử dụng ‘dynamic’ như một kiểu dữ liệu thông thường, rất tiện lợi thay vì viết các phương thức generic <T>. Ví dụ ta có phương thức sau để trả về kết quả của phép cộng hai đối tượng có kiểu bất kì:

static dynamic DynamicAdd(dynamic x, dynamic y)
{
    return x+y;
}
// …
dynamic a= DynamicAdd ("X","Y"); // valid
dynamic b= DynamicAdd (1,9); // valid
dynamic c= DynamicAdd (true,false); // RuntimeBinderException

Dynamic Programming và hiệu suất của ứng dụng

Trong phần giới thiệu về DLR ta thấy kĩ thuật lập trình mang lại khá nhiều lợi ích, tuy nhiên xét về mặt hiệu suất của ứng dụng thì sao? Như đã biết, các đối tượng được khai báo với dynamic chỉ được xác định trong quá trình runtime. Việc xác định và và ép về đúng kiểu dữ liệu của đối tượng thông qua DLR đòi hỏi thời gian và số lượng mã lệnh IL sinh ra nhiều hơn gấp vài lần so với sử dụng statically typed trong một ví dụ đơn giản như:

dynamic a = 1;
Console.WriteLine(a);

Qua kiểm tra thực tế giữa hai đoạn mã sau:

Đoạn mã 1:

dynamic x = 10;
dynamic y = x + 10;

Đoạn mã 2:

int x = 10;
int y = x + 10;

Thì đoạn mã 2 có thể chạy nhanh gấp hơn 30 lần so với đoạn mã 1.

Và nếu sửa ví dụ trên với 3 dòng mã như sau thì khác biệt này có thể lên trên 60 lần:

dynamic x = 10;
dynamic y = x + 10;
dynamic z = y + x;

Vậy bạn đã thấy ưu cũng như nhược điểm của dynamic programming, mặc dù là một kĩ thuật lập trình hay qua một chút phân tích lợi và hại tôi có thể đưa ra một lời khuyên cho bạn là: hãy hạn chế sử dụng ‘dynamic’ nếu bạn muốn ưu tiên tới tốc độ hoạt động của ứng dụng.

Một vài lưu ý

Bạn không nên nhầm lẫn giữa vardynamic. Các biến khai báo với var luôn phải xác định rõ kiểu trong quá trình phân tích và biên dịch. Nói một cách khác, var chỉ là từ khóa để khai báo một cách ngầm định kiểu dữ liệu và các kiểu anonymous.

Mặc dù dynamic có thể được sử dụng với một vài từ khóa đặc biệt dành cho kiểu dữ liệu, nhưng về bản chất cả var và dynamic đều là từ khóa, không phải là một kiểu dữ liệu.

dynamic x=default(dynamic);
Console.WriteLine(x==null); // true
Console.WriteLine(1 is dynamic); // true

Biểu thức ‘x is dynamic’ trên tương đương với ‘x is object’ và luôn trả về true với mọi giá trị khác null.

Phần kết

Bài viết chưa đủ cho bạn thấy hết những ưu điểm của Dynamic Programming và những kĩ thuật mới mà bạn có thể áp dụng vào trong lập trình. Trong bài sau, tôi sẽ giới thiệu về lớp ExpanoObject, DynamicObject và kĩ thuật Dynamic Method Bags. Với các đối tượng đó bạn có thể linh hoạt tạo thêm hoặc xóa bỏ các thành viên ra khỏi một đối tượng trong quá trình runtime.

5 bình luận về “DLR – Dynamic Programming: DLR và dynamic keyword trong C#

  1. Bài viết của bạn quá hay. Mình áp dụng nó vào việc lấy dữ liệu trong Entity Framework và làm việc rất tốt. Nhưng khổ nỗi là hễ bị sai thì nó không báo được. Mình đành dùng kết hợp Reflection để truyền tên bảng cần lấy.
    Có cách nào toàn việc vừa kiểm lỗi vừa đảm bảo việc sử dụng Dynamic không?

    • Khi làm việc với DLR thì người ta thường dùng reflection để kiểm tra trước khi thực hiện nên có lẽ chỉ còn cách này. Nếu được bạn có thể cho ví dụ cụ thể hơn để mình kiểm tra thử.

  2. Ah, mình phát hiện ra 1 điều hơi thú vị. Khi dùng Dynamic, không thể dùng cú pháp Lambda.
    Ví dụ:
    dynamic result = entities.Roles;
    dynamic result1 = entities.GetType().GetProperty(“Roles”).GetValue(entities,null);

    Mình không thể thực hiện cú pháp result1.Order(t=>t.RoleName). Vậy phải thực hiện bằng cách nào?

    • Đây là thiếu sót khi không đề cập đến vấn đề này trong bài viết. Kiểu dynamic không thể sử dụng được extension method, anonymous method, lambda expression. Do các chức năng này được compiler tự động sửa đổi thành mã C# tương ứng khi biên dịch, trong khi các đối tượng kiểu dynamic lại được binding trong lúc runtime.

      Đoạn trích sau nói rõ những hạn chế này của dynamic:

      – Dynamic binding will not be able to find extension methods. Whether extension methods apply or not depends on the static context of the call (i.e. which using clauses occur), and this context information is not kept as part of the payload.

      – Anonymous functions (i.e. lambda expressions) cannot appear as arguments to a dynamic operation. The compiler cannot bind (i.e. “understand”) an anonymous function without knowing what type it is converted to.

      (New Features in C# 4.0 – http://msdn.microsoft.com/en-us/vcsharp/ff628440)

      Cảm ơn bạn đã giúp bổ sung!

  3. Bạn ơi cho mình hỏi 1 lỗi khi dùng dynamic:
    Vd mình có 1 anonymous sau:
    var temp= new { d1=1,d2=2,d3=3};
    tại project 1 mình truyền temp qua một phương thức của project 2 (e.i (dynamic p), p nhận được dữ liệu của temp nhưng thông thể truy xuất vào bên trong (vd: p.d1: sẽ phát sinh lỗi).
    Cám ơn bạn trước.

Đã đóng bình luận.