Thứ bảy, ngày 3 tháng 12 năm 2016

.Net – Cơ bản về Expression Tree (C#)

Ngày đăng: 11/3/2012, 1:46:49AM | Lượt xem: 3,626
Hot!

Trước đây tôi từng giới thiệu về kĩ thuật xây dựng cây biểu thức (expression tree) từ những biểu thức toán học đơn giản. Trong .Net 3 bạn có thể thấy một API hoàn chỉnh cho phép bạn làm công việc tương tự nhưng với mức độ cao hơn nhiều thông qua các lớp thừa kế từ. Qua bài viết này, bạn sẽ biết cách để tạo một Expression Tree đồng thời biên dịch để sử dụng nó.  Một phần kiến thức bạn không thể thiếu để bắt đầu là Lambda expression, bạn có thể đọc tại bài viết sau: Tìm hiểu về Lambda Expression

Expression TreeTrước đây tôi từng giới thiệu về kĩ thuật xây dựng cây biểu thức (expression tree) từ những biểu thức toán học đơn giản. Trong .Net 3 bạn có thể thấy một API hoàn chỉnh cho phép bạn làm công việc tương tự nhưng với mức độ cao hơn nhiều thông qua các lớp thừa kế từ. Qua bài viết này, bạn sẽ biết cách để tạo một Expression Tree đồng thời biên dịch để sử dụng nó.  Một phần kiến thức bạn không thể thiếu để bắt đầu là Lambda expression, bạn có thể đọc tại bài viết sau: Tìm hiểu về Lambda Expression

Khảo sát namespace System.Linq.Expressions

 

API của Expression Tree được .Net cung cấp trong namespace System.Linq.Expressions. Cây phân cấp của các lớp trong namespace này có dạng sau:

System.Linq.Expressions - Hierarchy

Tên của các lớp trên rất rõ ràng và dễ hiểu, ví dụ như BinaryExpression là biểu thức gồm hai toán hạng và một toán tử tạo nên. Ở mức độ bắt đầu, bạn sẽ thấy việc tạo Expression Tree tương đối đơn giản, nếu như đã  hiểu qua cách sử dụng CodeDom thì bạn sẽ không gặp khó khăn gì khi làm việc với kĩ thuật này.

Lớp abstract Expression có hai thuộc tính mà ta cần phân biệt là Type và NodeType

  • Type: Kiểu trả về của Expression
  • NodeType: Là giá trị kiểu enum ExpressionType đại diện cho loại biểu thức. Ví dụ: Add, Equal, LessThan, Or, Power, Parameter…

Chú ý rằng các lớp con của Expression không định nghĩa bất kì phương thức khởi tạo nào. Vì thế để tạo bất kì một loại đối tượng Expression nào, bạn sử dụng các phương thức static tương ứng được cung cấp trong lớp Expression. Bạn sẽ thấy ví dụ ngay trong phần tiếp theo.

Tạo Expression Tree bằng API

 

Ví dụ bạn muốn tạo một biểu thức x + y với hai tham số là kiểu int:

ParameterExpression first = Expression.Parameter(typeof(int), "x");
ParameterExpression second = Expression.Parameter(typeof(int), "y");
BinaryExpression add = Expression.Add(first, second);

Phức tạp hơn một chút tôi có thể tạo biểu thức x + y * 3 với cách tương tự như trên:

ParameterExpression first = Expression.Parameter(typeof(int), "x");
ParameterExpression second = Expression.Parameter(typeof(int), "y");
ConstantExpression third = Expression.Constant(3);

BinaryExpression add = Expression.Add(first, second);
BinaryExpression mul = Expression.Multiply(add, third);

Như vậy bạn có thể tưởng tượng cây biểu thức được lưu trong đối tượng BinaryExpression mul trên như sau:

Expression Tree

Tạo Expression Tree từ Lambda expression

Cú pháp để tạo một Expression Tree từ Lambda expression rất đơn giản. Bạn phải khai báo một đối tượng thuộc lớp Expression<TDelegate> và gán cho nó một lambda expression.

Expression<Func<int, int, int>> expr = (x, y) => x + y;

Khi phân tích bằng một công cụ reflector hoặc disassembler, bạn có thể thấy rằng compiler tự động sinh ra mã IL tương tự với ví dụ ở phần trên nhưng rõ ràng cách viết này gọn và đơn giản hơn nhiều.

Tạo Expression<TDelegate> từ các kiểu Expression khác

Ở phần trên bạn đã biết cách tạo một đối tượng Expression<TDelegate> từ lambda expression. Mục đích chính để ta phải thực hiện bước này là vì lớp Expression<TDelegate> cung cấp phương thức để biên dịch Expression Tree thành một đối tượng Func<> mà ta dùng để thực thi. Chi tiết vấn đề này bạn sẽ thấy trong phần tiếp theo.

Vậy vấn đề ta cần quan tâm là làm sao chuyển một đối tượng từ kiểu Expression bất kì sang Expression<TDelegate>. Câu trả lời nằm ở lớp Expression, thông qua phương thức Lambda(), bạn có thể dễ dàng tạo một đối tượng Expression<TDelegate>. Overload của phương thức Lambda() mà tôi sẽ sử dụng có định nghĩa như sau:

Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters)

Như vậy ta sử dụng phương thức này tạo đối tượng Expression<TDelegate> cho đối tượng BinaryExpression add như sau:

ParameterExpression first = Expression.Parameter(typeof(int), "x");
ParameterExpression second = Expression.Parameter(typeof(int), "y");
BinaryExpression add = Expression.Add(first, second);

Expression<Func<int, int, int>> func = Expression.Lambda<Func<int, int, int>>(add, first, second);

Phương thức Lambda<TDelegate>() trên là một phương thức generic vì thế bạn cần xác định chính xác các tham số của kiểu Func<> tương ứng với đối tượng Expression mà ta truyền vào làm tham số. Ví dụ trên với đối tượng BinaryExpression add ta biết rõ nó cần hai tham số kiểu int và kiểu trả về là int, như thế kiểu Func<> tương ứng là Func<int,int,int>.

Thay vì sử dụng phương thức Lambda<TDelegate>() bạn có thể sử dụng một phiên bản khác của phương thức Lambda() với kiểu trả về là LambdaExpression:

ParameterExpression first = Expression.Parameter(typeof(int), "x");
ParameterExpression second = Expression.Parameter(typeof(int), "y");
BinaryExpression add = Expression.Add(first, second);

LambdaExpression func = Expression.Lambda(add, first, second);

Một ví dụ khác:

ParameterExpression first = Expression.Parameter(typeof(int), "x");
ParameterExpression second = Expression.Parameter(typeof(int), "y");
ConstantExpression third = Expression.Constant(3);

BinaryExpression add = Expression.Add(first, second);
BinaryExpression mul = Expression.Multiply(add, third);

LambdaExpression func = Expression.Lambda(mul, first, second);

Biên dịch Expression Tree

Expression  tree có thể được biên dịch để tạo một đối tượng Func<> và qua đó cho phép bạn thực hiện tính toán bằng cách truyền những tham số cần thiết cho đối tượng này. Để biên dịch ta cần sử dụng phương thức Compile của lớp LambdaExpression hoặc Expression<TDelegate>.

Quay lại ví dụ ở phần trước ta sẽ biên dịch Expression Tree và cho tính giá trị của biểu thức 1+2*3 (trong đó 3 là hằng số nên ta chỉ cần truyền hai số là 1 và 2):

ParameterExpression first = Expression.Parameter(typeof(int), "x");
ParameterExpression second = Expression.Parameter(typeof(int), "y");
ConstantExpression third = Expression.Constant(3);

BinaryExpression add = Expression.Add(first, second);
BinaryExpression mul = Expression.Multiply(add, third);

Với lớp LamdbaExpression:

Phương thức Compile() lớp này trả về một đối tượng Delegate:

LambdaExpression expr = Expression.Lambda(mul, first, second);

Delegate myDelegate = expr.Compile();

Để thực thi Delegate này với các tham số truyền vào, ta sử dụng phương thức DynamicInvoke() với kiểu trả về là object:

int result = (int)myDelegate.DynamicInvoke(4, 5);

// result = 27

Ba dòng trên có thể được viết gộp thành 1 dòng:

int result = (int)Expression.Lambda(mul, first, second).Compile().DynamicInvoke(4, 5);

Với lớp Expression<TDelegate>:

Expression&lt;Func&lt;int, int, int&gt;&gt; expr = Expression.Lambda&lt;Func&lt;int, int, int&gt;&gt;(mul, first, second);

Func&lt;int, int, int&gt; func = expr.Compile();

int result = func(4, 5);
// result = 27

Cách viết gọn:

int result = Expression.Lambda<Func<int, int, int>>(mul, first, second).Compile()(4, 5);

Phần kết

Như vậy bạn đã nắm được phần cơ bản về Expression Tree, những kiến thức này chưa đủ để bạn nhận thấy hết khả năng và công dụng của khái niệm này. Tuy nhiên từ sự khởi đầu này, bạn có thể tiếp tục tìm hiểu để biết cách tạo những Expression Tree phức tạp và ứng dụng trong những kĩ thuật như Linq hay Dynamic Language Runtime (DLR) mà tôi hi vọng sẽ dịp giới thiệu trong các bài viết sau này.

http://yinyangit.wordpress.com

 Chia sẻ qua: 
Hot!
Ý kiến bạn đọc

These items will be permanently deleted and cannot be recovered. Are you sure?

Gallery

image

Maecenas viverra rutrum pulvinar

Maecenas viverra rutrum pulvinar! Aenean vehicula nulla sit amet metus aliquam et malesuada risus aliquet. Vestibulum rhoncus, dolor sit amet venenatis porta, metus purus sagittis nisl, sodales volutpat elit lorem…

Read more

Text Links

Thiết kế logo chuyên nghiệp Insky
DAFABET
W88 w88b.com/dang-ky-tai-khoan-w88
W88
Copyright © 2011 - 2012 vietshare.vn by phamkhuong102@gmail.com doanhkisi2315@gmail.com. All rights reserved.