.NET – Tạo instance và chuyển đổi kiểu dữ liệu bất kì

dot_net_splashTrong bài viết này, tôi sẽ giới thiệu cách giải quyết hai vấn đề bạn có thể gặp phải khi làm việc với .NET:
– Tạo instance của một kiểu dữ liệu bất kì với .
– Chuyển đổi một đối tượng sang một kiểu dữ liệu bất kì.

Tạo instance của một kiểu dữ liệu bất kì

Việc tạo instance của một kiểu dữ liệu chỉ cần sử dụng phương thức Activator.CreateInstance(). Tuy nhiên đối với các kiểu dữ liệu không chứa parameterless constructor (phương thức khởi tạo không có tham số) thì phương thức này sẽ ném ra ngoại lệ MissingMethodException “No parameterless constructor defined for this object”.

Để giải quyết, tôi sẽ thay thế phương thứcc này bằng phương thức FormatterServices.GetUninitializedObject(). Tuy nhiên, phương thức vẫn không thể tạo được instance của kiểu string, vì vậy thay vì dùng reflection, tôi phải giải quyết bằng cách sau:

public static T CreateInstance<T>()
{
    var type = typeof(T);
    if (type == typeof(string))
    {
        return (T)(object)String.Empty;
    }
    else
    {
        return (T)FormatterServices.GetUninitializedObject(type);
    }
}

Chuyển đổi đối tượng sang kiểu dữ liệu bất kì

Với các kiểu dữ liệu nguyên thủy (primitive type), bạn có thể dùng phương thức Convert.ChangeType() để chuyển đổi. Với kiểu dữ liệu phức tạp, cách linh hoạt nhất là sử dụng reflection để lặp qua các property của đối tượng nguồn và gán cho property tương ứng của đối tượng đích.

public static TDest ChangeType<TDest>(object source)
{
    var destObj = CreateInstance<TDest>();

    var sourceType = source.GetType();
    var destType = typeof(TDest);

    if (sourceType.IsValueType || destType.IsValueType)
        return (TDest)Convert.ChangeType(source, destType);

    var properties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

    foreach (var prop1 in properties)
    {

        var prop2 = destType.GetProperty(prop1.Name);

		// destType does not has a property named prop1.Name
        if (prop2 == null)
            continue;

        var value = prop1.GetValue(source, null);

		// prop1 and prop2 are not same type
        if (prop1.PropertyType != prop2.PropertyType)
            continue;

        prop2.SetValue(destObj, value, null);
    }
    return destObj;
}

Bổ sung theo lời khuyên của anh Lê Hoàng Dũng(7 March, 2012):

Viết dưới dạng extension method với tên ToType():

static class Y2Extensions
{
    public static T CreateInstance<T>()
    {
        var type = typeof(T);
        if (type == typeof(string))
        {
            return (T)(object)String.Empty;
        }
        else
        {
            return (T)FormatterServices.GetUninitializedObject(type);
        }
    }

    public static TDest ToType<TDest>(this object source)
    {
        var destObj = CreateInstance<TDest>();

        var sourceType = source.GetType();
        var destType = typeof(TDest);

        if (sourceType.IsValueType || destType.IsValueType)
            return (TDest)Convert.ChangeType(source, destType);

        var properties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (var prop1 in properties)
        {

            var prop2 = destType.GetProperty(prop1.Name);

            // destType does not has a property named prop1.Name
            if (prop2 == null)
                continue;

            var value = prop1.GetValue(source, null);

            // prop1 and prop2 are not same type
            if (prop1.PropertyType != prop2.PropertyType)
                continue;

            prop2.SetValue(destObj, value, null);
        }
        return destObj;
    }

}

Kiểm tra kết quả

Để kiểm tra, tôi tạo hai class Foo và Bar sau:

class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime CreateDate { get; set; }
}

class Bar
{
    public int Id { get; set; }
    public string Name { get; set; }

    public string CreateDate { get; set; }

	// This class doest not has a parameterless constructor
	public Bar(int id) { }

    public override string ToString()
    {
        return String.Format("Bar: {0} - {1} - {2}",
            Id, Name, String.IsNullOrEmpty(CreateDate)?"N/A":CreateDate);
    }
}

Và phương thức Main():

static void Main(string[] args)
{
    Console.WriteLine("1111".ToType<int>()* 2);  // 2222

    var foo = new Foo { Id = 1, Name = "Foo1", CreateDate = DateTime.Today };

    var bar = foo.ToType<string>();

    Console.WriteLine(bar); // Bar: 1 - Foo1 - N/A

    Console.Read();
}

Output:

2222
Bar: 1 - Foo1 - N/A

YinYang’s Programming Blog

Advertisements

11 thoughts on “.NET – Tạo instance và chuyển đổi kiểu dữ liệu bất kì

    • Cảm ơn anh, lúc đầu em cũng định viết dưới dạng extension nhưng vì bài viết này chỉ nhằm mục đích nêu ý tưởng là chính, hơn nữa cũng có thể có các extension tương tự rồi. Còn về phần exception thì mình có thể dùng một built-in như InvalidCastException hay không? Em cũng chưa chú trọng và chưa có nhiều kinh nghiệm về việc thiết kế lắm.

      Trả lời
  1. Hi, Thanks a.
    Theo e được biết thì nó là lập trình generic, vậy cho e hỏi là cách lập trình đó có gì đặc biệt k? Liệu nếu nói về nó thì a có thể viết 1 bài về tổng quát vấn đề lập trình generic được k a?

    Trả lời
    • Lập trình generic có mục đích làm cho class có thể được sử dụng linh động hơn với nhiều kiểu dữ liệu. Ví dụ như phương thức ToType() để chuyển kiểu ở trên, nếu ko dùng generic, bạn có thể cần viết hàng loạt phương thức như ToInt(), ToDouble(), ToDemical(), ToDateTime(),… rất bất tiện.
      Trước đây mình cũng tính viết 1 vài bài về generic nhưng lại thôi vì các bài viết về generic trên mạng có rất nhiều.
      Thân!

      Trả lời
  2. Hì, thank a
    E sẽ tìm kiếm trên mạng thêm, thực sự thì e đọc blog của a là chính. Thông tin trên mạng thì nhiều nhưng viết lan man cho nên cũng hơi ngại tìm kiếm. Nhân tiện a có thể chia sẻ những điều cốt yếu cần phải học hoặc có để có thể giỏi trong lập trình. E mới bước vào ngành cntt nên cũng hơi bị bối rối, không hiểu làm sao có thể viết dc 1 chương trình hoàn chỉnh và phức tạp dc

    Trả lời
    • Ko kể đến năng lực hay lợi thế của mỗi người khi bắt đầu, trong bất kì lĩnh vực nào thì hai thứ cần nhất chính là: đam mê và cần cù. Có hai thứ này rồi thì bạn sẽ có thể 90% trở thành một lập trình viên tốt.
      Ngoài ra tiếng Anh là một yêu cầu rất quan trọng, tiếp đến chính là “Google is your friend”.
      Chúc bạn thành công!

      Trả lời

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s