Thứ hai, ngày 5 tháng 12 năm 2016

DLR– Dynamic Programming: Một wrapper của string với DynamicObject và reflection

Ngày đăng: 16/3/2012, 13:50:45AM | Lượt xem: 3,497
Hot!
tag ,

Như đã giới thiệu trong bài “ExpandoObject, DynamicObject và Dynamic Method Bag“, để minh họa rõ ràng hơn cho cách sử dụng DynamicObject, tôi đã tạo một wrapper của lớp string với tên gọi Y2String trong C#. Lớp này sẽ override các phương thức cần thiết của DynamicObject để sử dụng được các phương thức của string và bổ sung thêm một số tính năng khác.

Như đã giới thiệu trong bài “ExpandoObject, DynamicObject và Dynamic Method Bag“, để minh họa rõ ràng hơn cho cách sử dụng DynamicObject, tôi đã tạo một wrapper của lớp string với tên gọi Y2String trong C#. Lớp này sẽ override các phương thức cần thiết của DynamicObject để sử dụng được các phương thức của string và bổ sung thêm một số tính năng khác.

Nếu chưa rõ các cách sử dụng DynamicObject, vui lòng coi lại bài “ExpandoObject, DynamicObject và Dynamic Method Bag”, ngoài ra bạn cần tìm hiểu .Net reflection để lấy và sử dụng các property, method của một đối tượng.

Một số tính năng có thể không cần đến nhưng là một ví dụ tốt giúp bạn biết cách áp dụng DynamicObject.

Các tính năng của lớp Y2String:

-          Cho phép ép kiểu tường minh (explicit) và không tường minh sang các kiểu dữ liệu khác (int, double, float, string,…). Các kiểu dữ liệu muốn ép tường minh phải có phương thức static Parse(string), ví dụ Int32.Parse(string).

-          Sử dụng được các property, method của lớp string

-          Tính năng indexer giúp lấy và gán giá trị trực tiếp cho một phần tử của đối tượng. Mỗi phần tử là một character.

-          Tạo giá trị mới cho đối tượng bằng cách gọi tương tự delegate. Ví dụ str(123), ta sẽ có str = “123”.

-          Toán tử binary ‘*’ giúp tạo ra giá trị chuỗi mới bằng n lần chuỗi cũ (n là toán hạng thứ 2).

-          Toán tử unary ‘-‘ giúp đảo ngược giá trị chuỗi.

Cách chuyển đổi kiểu dữ liệu explicit trong ví dụ

Để giúp bạn dễ hiểu hơn tôi sẽ nói sơ qua cách mà lớp Y2String override phương thức TryConvert để chuyển đổi (ép) kiểu dữ liệu. Trước khi đọc tiếp phần này, bạn có thể xem qua phần bên dưới để có cái nhìn tổng quát về ví dụ chính của bài viết.

Tham số đầu tiên của phương thức TryConvert() là đối tượng ConvertBinder chứa thuộc tính boolean Explicit để xác định cách chuyển đổi kiểu được sử dụng.  Thuộc tính Type xác định kiểu dữ liệu cần chuyển qua. Ta cần kiểm tra kiểu dữ liệu này có phương thức Parse(string) không bằng cách sau:

MethodInfo method=binder.Type.GetMethod(“Parse”,new Type[]{typeof(string)});

Vì Parse() là một phương thức static nên bạn có thể truyền null vào tham số đầu tiên (đối tượng sẽ thực thi phương thức Parse()) của phương thức Invoke(), và tham số thứ hai là chuỗi cần chuyển kiểu.

result = method.Invoke(null, new string[]{ value });

Như bạn có thể suy luận, có thể ép Y2String sang bất kì kiểu gì có chứa phương thức static Parse(string).

dynamic str = new Y2String("1/1/2011");

DateTime date = (DateTime)str;
Console.WriteLine(date.ToLongDateString());

// Output: Saturday, January 01, 2011

Một ví dụ khác để bạn áp dụng phương pháp này với các kiểu dữ liệu tự tạo:

class Student
{
    public string Name { get; set; }
    public int Age { get; set; }

    public static Student Parse(object obj)
    {
        try
        {
            string s = obj.ToString();
            string[] str = s.Split('-');
            return new Student() { Name = str[0], Age = int.Parse(str[1]) };
        }
        catch
        {
            throw new FormatException("Input string was not in a correct format");
        }
    }
    public override string ToString()
    {
        return string.Format("[Name = {0}, Age = {1}]", Name, Age);
    }
}
class Program
{
    static void Main(string[] args)
    {

        dynamic str = new Y2String("YinYang-10");

        var student = (Student)str;
        Console.WriteLine(student);

        Console.Read();
    }
}
// Ouput: [Name = YinYang, Age = 10]

Nếu cố thực hiện ép sang kiểu dữ liệu không có phương thức Parse() phù hợp, bạn sẽ gặp exception:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException war unhanlled.

Cannot convert type ‘Y2String’ to ‘Student’

Mã nguồn hoàn chỉnh của lớp Y2String:

using System;
using System.Text;
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            dynamic str = new Y2String("123");

            // TryConvert
            int i = (int)str;           // i = 123      ; explicit
            double d = (double)str;     // d = 123      ; explicit
            string s = str;             // s = "123"    ; implicit

            // TryGetMember
            int length = str.Length;    // length = 3

            // TryInvokeMember
            s = str.Insert(3, "y");     // s = "123y", str = "123y"

            // TrySetIndex
            str[0] = 'y';               // str = "y23y"

            // TryInvoke
            s = str("abc");             // s = "abc", str="abc"
            // TryBinaryOperation
            str = str * 2;              // str = "abcabc"

            // TryUnaryOperation
            s = -str;                   // s = "cbacba"

            // Console.Read();
        }
    }

    class Y2String : DynamicObject
    {
        private string value;

        public Y2String(string value)
        {
            this.value = value;
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            MethodInfo method = typeof(string).GetMethod(binder.Name);
            if (method != null)
            {
                result = method.Invoke(this.value, args);
                this.value = result.ToString();
                return true;
            }
            result = null;
            return false;
        }

        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
        {
            result = this.value = args[0].ToString();
            return true;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            PropertyInfo prop = typeof(string).GetProperty(binder.Name);
            if (prop == null)
            {
                result = null;
                return false;
            }
            result = prop.GetValue(value, null);
            return true;
        }

        public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
        {
            Type type = value.GetType();
            if (type != typeof(char))
            {
                throw new InvalidCastException(String.Format("Cannot implicitly convert type '{0}' to 'char'", type.Name));
            }
            char[] chars = this.value.ToCharArray();
            chars[(int)indexes[0]] = (char)value;
            this.value = new string(chars);
            return true;
        }

        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
        {
            result = this.value[(int)indexes[0]];
            return true;
        }

        public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
        {

            if (binder.Operation == ExpressionType.Multiply)
            {
                StringBuilder str = new StringBuilder();
                for (int i = 0; i < (int)arg; i++)
                {
                    str.Append(value);
                }
                result = new Y2String(str.ToString());
                return true;
            }
            result = null;
            return false;
        }

        public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result)
        {

            if (binder.Operation == ExpressionType.Negate)
            {
                result = this.value = new string(this.value.Reverse().ToArray());
                return true;
            }
            result = null;
            return false;
        }

        public override bool TryConvert(ConvertBinder binder, out object result)
        {
            result = null;
            if (!binder.Explicit)
            {
                result = value;
            }
            else
            {
                MethodInfo method=binder.Type.GetMethod("Parse",new Type[]{typeof(string)});
                if (method != null)
                {
                    result = method.Invoke(null, new string[]{ value });
                }
            }
            return result!=null;
        }
        public override string ToString()
        {
            return value;
        }

    }
}

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.