2013-08-09 13 views
7

Câu hỏi của tôi liên quan đến việc kiểm tra kiểu trong một chuỗi các phương pháp chung. Giả sử tôi có một phương thức mở rộng cố chuyển đổi một mảng byte thành int, decimal, string hoặc DateTime.Tránh kiểm tra loại quá mức trong các phương pháp chung?

public static T Read<T>(this ByteContainer ba, int size, string format) where T : struct, IConvertible 
{ 
    var s = string.Concat(ba.Bytes.Select(b => b.ToString(format)).ToArray());    
    var magic = FromString<T>(s); 
    return (T)Convert.ChangeType(magic, typeof(T)); 
} 

Gọi phương thức này là FromString dịch chuỗi được ghép vào một loại cụ thể. Thật không may, logic kinh doanh là hoàn toàn phụ thuộc vào loại T. Vì vậy, tôi kết thúc với một khối if-else Megalithic:

private static T FromString<T>(string s) where T : struct 
{ 
    if (typeof(T).Equals(typeof(decimal))) 
    { 
     var x = (decimal)System.Convert.ToInt32(s)/100; 
     return (T)Convert.ChangeType(x, typeof(T)); 
    } 
    if (typeof(T).Equals(typeof(int))) 
    { 
     var x = System.Convert.ToInt32(s); 
     return (T)Convert.ChangeType(x, typeof(T)); 
    } 
    if (typeof(T).Equals(typeof(DateTime))) 
     ... etc ... 
} 

Tại thời điểm này, tôi muốn nhiều phương pháp có cùng tên và các loại khác nhau trở lại, một cái gì đó dọc theo dòng này:

// <WishfulThinking> 
private static decimal FromString<T>(string s) 
{ 
    return (decimal)System.Convert.ToInt32(s)/100; 
}  
private static int FromString<T>(string s) 
{ 
    return System.Convert.ToInt32(s); 
} 
// </WishfulThinking> 

... nhưng tôi nhận ra điều này là không hợp lệ, như T không thể được hạn chế đến một loại hình cụ thể, và không có nó, tất cả các phương pháp này sẽ có mâu thuẫn tương tự Chữ ký.

Có cách nào khả thi để triển khai FromString mà không cần kiểm tra loại quá mức không? Hoặc có thể có cách nào tốt hơn để tiếp cận vấn đề này hoàn toàn không?

+0

Điều này có vẻ như một vấn đề bạn có thể giải với biểu thức. – lukegravitt

Trả lời

10

Hoặc có thể có thể là một cách tốt hơn để tiếp cận vấn đề này hoàn toàn?

Chắc chắn, có một: bạn có thể làm cho mỗi chuyển đổi thành một lambda, làm một cuốn từ điển của họ, và sử dụng chúng cho việc chuyển đổi, như thế này:

private static IDictionary<Type,Func<string,object>> Converters = new Dictionary<Type,Func<string,object>> { 
    {typeof(int), s => Convert.ChangeType(System.Convert.ToInt32(s), typeof(int))} 
, {typeof(decimal), s => Convert.ChangeType((decimal)System.Convert.ToInt32(s)/100, typeof(decimal))} 
, ... // And so on 
}; 
public static T Read<T>(this ByteContainer ba, int size, string format) where T : struct, IConvertible { 
    Func<string,object> converter; 
    if (!Converters.TryGetValue(typeof(T), out converter)) { 
     throw new ArgumentException("Unsupported type: "+typeof(T)); 
    } 
    var s = string.Concat(ba.Bytes.Select(b => b.ToString(format)).ToArray()); 
    return (T)converter(s); 
} 
+2

Tôi nghĩ rằng cách tiếp cận này chỉ thổi tâm trí của tôi. Tôi sẽ cần phải dành một chút thời gian với điều này ... – MadHenchbot

+0

Thay vì sử dụng một từ điển tĩnh, làm thế nào về việc sử dụng một lớp tĩnh chung riêng 'MyConverters ' với một trường kiểu 'Func ' mà mặc định là một phương thức sẽ kiểm tra xem liệu 'T' có phải là một kiểu đã biết và ném một ngoại lệ hoặc cập nhật trường đó cho phù hợp không? Sau đó, 'Read ' có thể gọi 'MyConverters .Converter' mà không cần bất kỳ logic điều kiện nào sau lần sử dụng đầu tiên. – supercat

+0

@dasblinkenlight Cảm ơn bạn đã cho tôi biết ý tưởng này!Giải pháp của bạn đã giải quyết được vấn đề của tôi và khiến tôi suy nghĩ về C# theo một cách mới và thú vị. Nhiều đánh giá cao. – MadHenchbot

6

Nói chung, nếu bạn phải viết logic phải luôn kiểm tra loại tham số kiểu chung, bạn không thực sự viết mã có lợi từ việc chung chung. Trong trường hợp đó, và giả định vấn đề thực tế bạn đang cố gắng giải quyết là cần chuyển đổi một mảng byte thành một kiểu được xây dựng sẵn có thể dự đoán được, nó khuyên bạn nên bỏ phương pháp này và sử dụng các phương thức trong BitConverter class.

Tại thời điểm bạn có thể xác định giá trị của T, chỉ cần gọi phương thức thích hợp trên lớp BitConverter.

Cập nhật: Nếu một giải pháp chung là cần thiết, tôi muốn đề nghị một cái gì đó tương tự như câu trả lời dasblinkenlight 's, mặc dù tôi sẽ cho phép người gọi tiêm bộ chuyển đổi (sau khi tất cả, người gọi biết loại quả cần thiết) , mà tránh được vấn đề của việc duy trì một danh sách các chức năng chuyển đổi cùng với phương thức chung:

public static T Read<T>(this ByteContainer ba, int size, string format, 
         Func<string, T> converter) where T : struct, IConvertible 
{ 
    var s = string.Concat(ba.Bytes.Select(b => b.ToString(format)).ToArray()); 
    return converter(s); 
} 
+0

Tôi chắc chắn đồng ý với bạn. Thật không may, có rất nhiều quá trình dịch thuật liên quan đến việc chuyển đổi mà tôi không chắc chắn BitConverter sẽ đủ trong trường hợp của tôi. – MadHenchbot