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

DLR Dynamic Programming: ExpandoObject, DynamicObject và Dynamic MethodBags

Ngày đăng: 4/12/2012, 0:0:0AM | Lượt xem: 1,570
Hot!
tag

Trong bài giới thiệu về dynamic keyword, bạn đã thấy rằng dynamic programming giúp cho việc lập trình trở nên dễ dàng và tiện lợi hơn. Tuy nhiên không chỉ đơn giản là tự động xác định các kiểu dữ liệu, dynamic programming cho phép bạn tạo các thành viên của đối tượng và sử dụng chúng trong quá trình runtime.

 

Trong bài giới thiệu về dynamic keyword, bạn đã thấy rằng dynamic programming giúp cho việc lập trình trở nên dễ dàng và tiện lợi hơn. Tuy nhiên không chỉ đơn giản là tự động xác định các kiểu dữ liệu, dynamic programming cho phép bạn tạo các thành viên của đối tượng và sử dụng chúng trong quá trình runtime.

 

Một điểm chung là khi bạn tạo các đối tượng ExpandoObject và DynamicObject (mà tôi sẽ giới thiệu dưới đây) theo cách thông thường, bạn sẽ thấy rằng đối tượng này chẳng có tác dụng gì. Vì vậy khi nói đến Dynamic programming, bạn hãy dùng từ khóa dynamic thay cho các từ khóa khai báo kiểu dữ liệu khác.

Expando Object

Bản chất của ExpandoObject là một lớp được  implement các interface ICollection, IDictionary, IEnumerable để quản lý các giá trị được thêm vào theo cặp <string, object>.

Các property tự động xác định kiểu khi bạn tạo ra chúng. Khi để chuột vào một property của đối tượng ExpandoObject, bạn có thể thấy rằng nó cũng được khai báo với dynamic.

Ví  dụ thêm hai property Name và Age:

 

dynamic expo = new ExpandoObject();
expo.Name = "Yin Yang";
expo.Age = 100;
Console.WriteLine(expo.Name);
Console.WriteLine(expo.Age*2);

 

Output:

Yin Yang
200

Không chỉ gán các giá trị dữ liệu thông thường, bạn có thể gán một delegate cho đối tượng để sử dụng như một phương thức. Nếu chưa quen thuộc với cú pháp tôi sử dụng dưới đây, bạn có thể xem bài viết về Lambda Expression.

 

dynamic expo = new ExpandoObject();
expo.Power = (Func<double, double, double>)((x, y) => Math.Pow(x, y));
expo.View = (Action)(() => Console.WriteLine(expo.Power));

Console.WriteLine(expo.Power(2,10));
expo.View();

 

Ouput:

1024

System.Func`3[System.Double,System.Double,System.Double]

Dynamic Object

Để sử dụng lớp này bạn bắt buộc phải tạo một lớp thừa kế từ nó, sau đó override các phương thức tùy theo mục đích bạn cần sử dụng. Linh hoạt hơn ExpandoObject, bạn không chỉ thêm các thành viên cho đối tượng mà còn xử lý được các toán tử binary, unary, ép kiểu, …

Các phương thức mà bạn có thể override để xử lý cho từng trường hợp mà đối tượng được sử dụng có tên bắt đầu bằng Try:

Name Description
TryBinaryOperation Được thực thi khi sử dụng các phép toán toán tử hai ngôi như +, -, *, /.  Ví dụ như x+y thì x (left hand side) chính là đối tượng được thực thi phương thức này, với tham số là y.
TryConvert Thực thi khi dùng các phép chuyển đổi kiểu, bao gồm không tường minh. Ví dụ:(int)x // explicity = x   // implicit
TryCreateInstance This method is not intended for use in C# or Visual Basic.
TryDeleteIndex This method is not intended for use in C# or Visual Basic.
TryDeleteMember This method is not intended for use in C# or Visual Basic.
TryGetIndex Dùng với tính năng indexer, lấy giá trị tại vị trí xác định. Ví dụ:x[i]Mặc dù tham số thứ hai indexes của phương thức là một mảng các index cần lấy giá trị, nhưng trong C# ta chỉ được sử dụng phần tử đầu tiên: indexes[0].
TryGetMember Lấy giá trị property của đối tượng. Ví dụ:x.Length
TryInvoke Thực thi đối tượng như một delegate. Ví dụ:x(10)
TryInvokeMember Thực thi một thành viên của đối tượng, như method. Ví dụ:x.Test()
TrySetIndex Dùng với tính năng indexer, gán giá trị tại vị trí xác định
TrySetMember Gán giá trị cho một thành viên của đối tượng. Ví dụ:x.Name=”Yin”
TryUnaryOperation Thực thi khi dùng toán tử unary với đối tượng. Ví dụ:!x-x

Lớp này rất thích hợp để tạo ra wrapper cho một lớp khác. Thay vì dùng các tính năng indexer, operator overloading, viết thêm các method, property thì ta chỉ cần override các phương thức. Mặc dù có vài khuyết điểm như làm giảm tốc độ thực thi và không hỗ trợ Intellisense, nhưng đây là một tính năng đáng để bạn thử.

Ví dụ sau tạo ra một lớp StudentList là wrapper của List<Student> và thừa kế từ DynamicObject. Với hai phương thức được override là TryInvokeMember() và TryGetIndex() , bạn có thể sử dụng các phương thức của List<Student> và lấy giá trị của phần tử trong List<Student> bằng indexer.

Bạn có thể cũng cần tham khảo các bài viết về reflection và LINQ nếu như chưa hiểu rõ cú pháp sử dụng trong ví dụ.

 

class Student
{
    public string ID { get; set; }
    public string Name { get; set; }

    public override string ToString()
    {
        return String.Format("Student: ID = {0}, Name = {1}", ID, Name);
    }
}

class StudentList : DynamicObject
{
    IList<Student> students;

    public StudentList()
    {
        students = new List<Student>();
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        MethodInfo method = students.GetType().GetMethod(binder.Name);

        if (method != null)
        {
            result = method.Invoke(students, args);
            return true;
        }
        result = null;
        return false;
    }

    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        if (indexes[0].GetType() == typeof(int)) // Lấy student tại index
        {
            result = students[(int)indexes[0]];
        }
        else    // lấy student đầu tiên có ID phù hợp
        {
            string id = indexes[0].ToString();
            result = (from st in students
                        where st.ID == id
                        select st).First();
        }
        return true;
    }
}

 

Phương thức TryGetIndex() sẽ hỗ trợ lấy phần tử trong đối tượng students bằng indexer theo hai cách: dựa vào index (int) và ID (string).

Phương thức Main() để kiểm tra sẽ cho ra kết quả đúng như ta mong đợi:

 

class Program
{
    static void Main(string[] args)
    {
        dynamic students = new StudentList();
        students.Add(new Student() { ID = "1", Name = "Bo" });
        students.Add(new Student() { ID = "2", Name = "Bi" });

        Console.WriteLine(students["1"]); // ID
        Console.WriteLine(students[1]); // index

        Console.Read();
    }
}

 

Ouput:

Student: ID = 1, Name = Bo

Student: ID = 2, Name = Bi

Trong bài viết sau tôi sẽ giới thiệu một wrapper của string để minh họa đầy đủ hơn lớp DynamicObject này.

Dynamic Method Bag

Kĩ thuật này được giới thiệu trên MSDN bởi tác giả Bill Wagner. Dynamic Method Bag sử dụng DynamicObject kết hợp giữa generic, Expression Treedynamic để tạo một “method bag”- một kiểu đối tượng cho phép bạn thêm phương thức và sử dụng chúng trong quá trình runtime.

Về bản chất đối tượng Method Bag sẽ lưu trữ các delegate và Expression Tree trong một collection tương tự như ExpandoObject. Hiểu biết về các kĩ thuật này, bạn hoàn toàn có thể tự viết một Method Bag tương tự mà không cần xem qua bài viết này (http://msdn.microsoft.com/library/ee658247).

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.