2010-01-21 27 views
8

Tôi đang làm việc trên một điều khiển có thể thực hiện một số kiểu dữ liệu khác nhau (bất kỳ thứ gì triển khai IComparable).Chuyển đổi loại chung mà không có rủi ro Ngoại lệ

tôi cần để có thể so sánh chúng với một biến khác thông qua vào.

Nếu datatype chính là một DateTime, và tôi đã thông qua một String, tôi cần phải

  • nỗ lực để chuyển đổi Chuỗi thành một DateTime để thực hiện so sánh ngày tháng.
  • nếu không thể chuyển đổi chuỗi thành DateTime, sau đó thực hiện so sánh Chuỗi.

Vì vậy, tôi cần một cách tổng quát để cố gắng chuyển đổi từ bất kỳ loại nào thành bất kỳ loại nào. Dễ dàng, .Net cung cấp cho chúng tôi lớp học TypeConverter.

Bây giờ, tốt nhất tôi có thể làm việc để xác định xem Chuỗi có thể được chuyển đổi thành DateTime hay không là sử dụng ngoại lệ. Nếu ConvertFrom đưa ra một ngoại lệ, tôi biết tôi không thể thực hiện chuyển đổi và phải thực hiện so sánh chuỗi.

Sau đây là tốt nhất mà tôi nhận được:

 string theString = "99/12/2009"; 
     DateTime theDate = new DateTime (2009, 11, 1); 

     IComparable obj1 = theString as IComparable; 
     IComparable obj2 = theDate as IComparable; 

     try 
     { 
      TypeConverter converter = TypeDescriptor.GetConverter (obj2.GetType()); 
      if (converter.CanConvertFrom (obj1.GetType())) 
      { 
       Console.WriteLine (obj2.CompareTo (converter.ConvertFrom (obj1))); 
       Console.WriteLine ("Date comparison"); 
      } 
     } 
     catch (FormatException) 
     { 
      Console.WriteLine (obj1.ToString().CompareTo (obj2.ToString())); 
      Console.WriteLine ("String comparison"); 
     } 

Một phần của tiêu chuẩn của chúng tôi ở trạng thái công việc mà:

Exceptions chỉ nên được nâng lên khi một tình huống ngoại lệ - tức. một lỗi gặp phải.

Nhưng đây không phải là tình huống đặc biệt. Tôi cần một cách khác xung quanh nó.

Hầu hết các loại biến có phương thức TryParse trả về boolean để cho phép bạn xác định xem chuyển đổi có thành công hay không. Nhưng không có phương thức TryConvert nào có sẵn cho TypeConverter. CanConvertFrom chỉ làm mờ nếu có thể chuyển đổi giữa các loại này và không xem xét dữ liệu thực được chuyển đổi. Phương pháp IsValid cũng vô ích.

Bất kỳ ý tưởng nào?

EDIT

tôi không thể sử dụng AS và IS. Tôi không biết một trong hai kiểu dữ liệu ở thời gian biên dịch. Vì vậy, tôi không biết những gì để As và là để !!!

EDIT

Ok đóng đinh khốn. Nó không gọn gàng như Marc Gravells, nhưng nó hoạt động (tôi hy vọng). Cảm ơn bạn đã inpiration Marc. Sẽ làm việc trên làm sạch nó lên khi tôi nhận được thời gian, nhưng tôi đã có một chút stackfixes mà tôi có để có được trên với.

public static class CleanConverter 
    { 
     /// <summary> 
     /// Stores the cache of all types that can be converted to all types. 
     /// </summary> 
     private static Dictionary<Type, Dictionary<Type, ConversionCache>> _Types = new Dictionary<Type, Dictionary<Type, ConversionCache>>(); 

     /// <summary> 
     /// Try parsing. 
     /// </summary> 
     /// <param name="s"></param> 
     /// <param name="value"></param> 
     /// <returns></returns> 
     public static bool TryParse (IComparable s, ref IComparable value) 
     { 
      // First get the cached conversion method. 
      Dictionary<Type, ConversionCache> type1Cache = null; 
      ConversionCache type2Cache = null; 

      if (!_Types.ContainsKey (s.GetType())) 
      { 
       type1Cache = new Dictionary<Type, ConversionCache>(); 

       _Types.Add (s.GetType(), type1Cache); 
      } 
      else 
      { 
       type1Cache = _Types[s.GetType()]; 
      } 

      if (!type1Cache.ContainsKey (value.GetType())) 
      { 
       // We havent converted this type before, so create a new conversion 
       type2Cache = new ConversionCache (s.GetType(), value.GetType()); 

       // Add to the cache 
       type1Cache.Add (value.GetType(), type2Cache); 
      } 
      else 
      { 
       type2Cache = type1Cache[value.GetType()]; 
      } 

      // Attempt the parse 
      return type2Cache.TryParse (s, ref value); 
     } 

     /// <summary> 
     /// Stores the method to convert from Type1 to Type2 
     /// </summary> 
     internal class ConversionCache 
     { 
      internal bool TryParse (IComparable s, ref IComparable value) 
      { 
       if (this._Method != null) 
       { 
        // Invoke the cached TryParse method. 
        object[] parameters = new object[] { s, value }; 
        bool result = (bool)this._Method.Invoke (null, parameters); 

        if (result) 
         value = parameters[1] as IComparable; 

        return result; 
       } 
       else 
        return false; 

      } 

      private MethodInfo _Method; 
      internal ConversionCache (Type type1, Type type2) 
      { 
       // Use reflection to get the TryParse method from it. 
       this._Method = type2.GetMethod ("TryParse", new Type[] { type1, type2.MakeByRefType() }); 
      } 
     } 
    } 
+0

Không thực sự, có những câu hỏi hiểu biết những gì loại sẽ được chuyển đổi sang. Ở đây tôi không. Câu trả lời cho câu hỏi đó không giúp tôi chút nào. –

+0

Tốt. Đủ công bằng. – jason

+0

Phew .. Tôi nghĩ rằng tôi sẽ nhận được câu hỏi của tôi đóng cửa. Ive đã dành giờ trên này .. ;-) –

Trả lời

10

Có phải generics là một lựa chọn? Dưới đây là một hack táo bạo rằng săn phương pháp TryParse và gọi đó là thông qua một (cached) đại biểu:

using System; 
using System.Reflection; 

static class Program 
{ 
    static void Main() 
    { 
     int i; float f; decimal d; 
     if (Test.TryParse("123", out i)) { 
      Console.WriteLine(i); 
     } 
     if (Test.TryParse("123.45", out f)) { 
      Console.WriteLine(f); 
     } 
     if (Test.TryParse("123.4567", out d)) { 
      Console.WriteLine(d); 
     } 
    } 
} 
public static class Test 
{ 
    public static bool TryParse<T>(string s, out T value) { 
     return Cache<T>.TryParse(s, out value); 
    } 
    internal static class Cache<T> { 
     public static bool TryParse(string s, out T value) 
     { 
      return func(s, out value); 
     }  
     delegate bool TryPattern(string s, out T value); 
     private static readonly TryPattern func; 
     static Cache() 
     { 
      MethodInfo method = typeof(T).GetMethod(
       "TryParse", new Type[] { typeof(string), typeof(T).MakeByRefType() }); 
      if (method == null) { 
       if (typeof(T) == typeof(string)) 
        func = delegate(string x, out T y) { y = (T)(object)x; return true; }; 
       else 
        func = delegate(string x, out T y) { y = default(T); return false; }; 
      } else { 
       func = (TryPattern) Delegate.CreateDelegate(typeof(TryPattern),method); 
      }    
     } 
    } 
} 
+0

Vâng, wow trông chỉ là vé! Cảm ơn. Sẽ chỉ phải đợi đến thứ hai để thử nó tại nơi làm việc. Thích nó! –

+0

Bummer bị từ chối .. Giải pháp có vẻ tốt, nhưng tôi chỉ được truyền dữ liệu dưới dạng đối tượng. Tôi không biết các loại lúc biên dịch. Tôi nhận ra tôi đã không xây dựng câu hỏi của tôi rất tốt, do đó sự nhầm lẫn. Điều này là gần mặc dù .. rất rất gần, tôi vẫn có thể có một đi bằng cách sử dụng một phần phản ánh của nó Im chắc chắn. –

+0

Woohoo! Đã xoắn nó một chút để đến với giải pháp. Xem chỉnh sửa trong bài đăng của tôi. Cảm ơn nhiều! –

0

Vì vậy, tôi cần một cách tổng quát để cố gắng để chuyển đổi từ bất kỳ loại cho bất kỳ loại. Dễ dàng, .Net cung cấp cho chúng tôi lớp TypeConverter.

Bạn đang hỏi quá nhiều.

class Animal { } 
class Dog : Animal { } 
class Cat : Animal { } 

Tôi có nên có thể chuyển đổi một Cat đến một Dog?

Bạn sẽ thấy vấn đề của mình dễ giải quyết hơn nếu bạn chỉ định chính xác hơn (tốt hơn là chính xác) những gì bạn muốn hành vi của phương thức. Vì vậy, viết ra các đầu vào mong muốn và những gì bạn muốn đầu ra được trong mỗi trường hợp có thể. Sau đó, phương pháp của bạn nên tự viết.

Vì vậy, ngay bây giờ chúng ta có đặc điểm kỹ thuật này:

Nếu datatype chính là một DateTime, và tôi đã thông qua một String, tôi cần phải

nỗ lực để chuyển đổi String đến một DateTime để thực hiện a so sánh Date. nếu không thể chuyển đổi String thành DateTime thì thực hiện so sánh String.

int CompareTo(DateTime d, object o) { 
    string s = o as string; 
    if(s != null) { 
     DateTime dt; 
     if(dt.TryParse(s, out dt)) { 
      return d.CompareTo(dt); 
     } 
     else { 
      return d.ToString().CompareTo(s); 
     } 
    } 
    throw new InvalidOperationException(); 
} 
+0

Tôi muốn một phương thức bool TypeConverter.TryConvert (đối tượng inObject, out object ConvertedObjected). Kinda như DateTime.TryParse. Tôi muốn làm chính xác những gì mẫu mã tôi đã đăng, nhưng không có ngoại lệ được nêu ra. Tôi không thực sự làm các đối tượng ở đây, chỉ là các loại biến cơ bản: ints, strings, DateTimes .. Tôi nghĩ câu hỏi của tôi khá rõ ràng. –

+0

@Pongus: Không rõ. Bạn đang cố chuyển đổi 'inObject' thành loại gì? Bạn đang cố gắng chuyển đổi thành loại 'ConvertedObjected' (sic)? – jason

+0

lời xin lỗi của tôi. Nó là một điều khiển lưới. Tôi cần phải sắp xếp và lọc trên bất kỳ loại dữ liệu nào được ném vào tôi miễn là nó là IComparable. –

4

tôi cho rằng mã này thực sự cần ném ngoại lệ khi nó không thể hình dung ra một sự chuyển đổi. Nếu hai đối số được truyền vào là DateTime.NowColor.Fuschsia, bạn không thể so sánh ý nghĩa giữa chúng, vì vậy bất kỳ giá trị nào bạn trả lại đều sai. Đó là định nghĩa đúng thời điểm để ném một ngoại lệ.

Nếu bạn hoàn toàn cần tránh ngoại lệ, bạn không thể thực hiện những gì bạn muốn với các loại tùy ý. Mỗi loại có các quy tắc riêng về các giá trị mà nó có thể phân tích cú pháp và trình chuyển đổi không có cách nào để báo trước điều đó. (Có nghĩa là, như bạn đã nhận thấy, bạn biết rằng đôi khi bạn có thể chuyển đổi một số string thành DateTime, nhưng không được thiết kế để biết rằng "1/1/2010" là DateTime hợp lệ trong khi "Fred" không .)

+0

Trong trường hợp này, nó hoàn toàn hợp lệ cho các đầu vào là DateTime và Color. Rõ ràng là các kiểu dữ liệu thô của chúng không thể so sánh được. Đó là lý do tại sao tôi muốn kiểm tra xem liệu chúng có thể được chuyển đổi để có thể so sánh hữu ích. Tôi chỉ muốn kiểm tra, tôi không muốn điều này là một điều kiện lỗi. –

+0

Vì vậy, những gì bạn muốn chỉ là một phương pháp để kiểm tra xem một chuyển đổi sẽ thành công, mà không thực sự làm việc chuyển đổi? Điều này là không thể với các loại tùy ý. 'TypeConverter' được thiết kế để cố gắng chuyển đổi và ném ngoại lệ về lỗi. Bạn có thể kiểm tra các loại phổ biến mà bạn biết cách xử lý, như 'DateTime' và' Double' và như vậy, và gọi phương thức 'TryParse' của chúng. Nhưng điều đó chỉ hoạt động đối với các loại bạn kiểm tra. – Auraseer

5

Nếu nó không thể viết nó mà không có ngoại lệ, bạn có thể cô lập mã có vấn đề bằng refactoring nó thành một phương pháp như vậy:

public static bool TryConvert<T, U>(T t, out U u) 
{ 
    try 
    { 
     TypeConverter converter = TypeDescriptor.GetConverter(typeof(U)); 
     if (!converter.CanConvertFrom(typeof(T))) 
     { 
      u = default(U); 
      return false; 
     } 
     u = (U)converter.ConvertFrom(t); 
     return true; 
    } 
    catch (Exception e) 
    { 
     if (e.InnerException is FormatException) 
     { 
      u = default(U); 
      return false; 
     } 

     throw; 
    } 
} 

Lý tưởng nhất là bạn nên đi qua trong các loại nullable như đầu ra tham số, sao cho null đại diện cho giá trị không xác định (vì nó không thể thực hiện chuyển đổi) thay vì giá trị mặc định (ví dụ 0 cho int)

Các vấn đề liên quan