2010-01-21 61 views
8

我上控制工作,可以採取多種不同的數據類型(任何實現IComparable的)的。一般類型的轉換,而不用擔心例外

我需要能夠在通過另一個變量來比較這些。

如果主數據類型是一個DateTime,和我通過一個字符串,我需要

  • 嘗試轉換將字符串轉換爲日期時間以執行日期比較。
  • 如果String不能轉換爲DateTime然後做一個字符串比較。

所以我需要一個通用的方法來嘗試從任何類型轉換爲任何類型。很簡單,.Net爲我們提供了TypeConverter類。

現在,我可以工作要做,以確定是否該字符串可以轉換爲DateTime最好是使用異常。如果ConvertFrom引發異常,我知道我無法進行轉換,必須進行字符串比較。

以下是我得到的最好:

 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"); 
     } 

我們工作國家標準部分,其中:

例外只應在異常情況下提出的 - 即。遇到錯誤。

但是,這不是一個特殊的情況。我需要另一種方式。

大多數變量類型有一個TryParse方法,它返回一個布爾值,讓你決定是否轉換成功與否。但是沒有TryConvert方法可用於TypeConverter。 CanConvertFrom只有當它是可能的這些類型之間的轉換和犯規考慮要轉換的實際數據dermines。 IsValid方法也沒用。

任何想法?

編輯

我不能使用AS和IS。我不知道編譯時的數據類型。所以我不知道該怎麼做,是!

編輯

好釘的私生子。它不像Marc Gravells那樣整潔,但它有效(我希望)。感謝Marc的啓發。當我得到時間的時候,我會整理它,但我有一堆錯誤修正,我必須得到。

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

不是真的有問題知道什麼類型將被轉換爲。在這裏,我不。這個問題的答案根本不能幫助我。 – 2010-01-21 17:24:34

+0

好。很公平。 – jason 2010-01-21 17:27:02

+0

唷..我以爲我會讓我的問題關閉。我花了幾個小時在這一個.. ;-) – 2010-01-21 17:28:37

回答

10

泛型是一種選擇嗎?下面是狩獵的TryParse方法,並通過(緩存)代表稱這是厚臉皮的黑客:

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

是的,哇,看起來只是票!謝謝。將不得不等到星期一才能在工作中嘗試。喜歡它! – 2010-01-22 22:48:41

+0

Bummer否認..該解決方案看起來不錯,但我只是作爲對象傳遞數據。編譯時我不知道類型。我意識到我沒有很好地提出我的問題,因此混亂。這雖然接近,但非常接近,我仍然可以使用它的反射部分。 – 2010-01-25 09:45:58

+0

嗚呼!已經扭轉了一下,想出解決方案。看到我的文章中的編輯。非常感謝! – 2010-01-25 12:42:21

0

所以我需要嘗試從任何類型的任何類型轉換的一般方法。很簡單,.Net爲我們提供了TypeConverter類。

你問的太多了。

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

我應該能夠轉換CatDog

如果您更精確地指定(更確切地說)您想要的方法的行爲,您會發現您的問題更容易解決。因此,請寫下預期的輸入,並在每種可能的情況下輸出結果。那麼你的方法應該自己寫。

所以現在我們有這個規格:

如果主數據類型是一個DateTime,而我通過了String,我需要

試圖將String轉換爲DateTime執行一個Date比較。 如果String無法轉換爲DateTime,則執行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

我想要一個布爾TypeConverter.TryConvert一個TryConvert方法(對象inObject,out對象ConvertedObjected)方法。有點像DateTime.TryParse。 我想做的是我發佈的代碼示例,但沒有提出異常。 我不是真的做的物體在這裏,只是基本的變量類型:整數,字符串,DateTime是否.. 我想我的問題是相當明確的。 – 2010-01-21 17:33:14

+0

@Pongus:現在還不清楚。你試圖將'inObject'轉換爲什麼類型?你想轉換爲'ConvertedObjected'類型嗎(原文如此)? – jason 2010-01-21 17:37:58

+0

我的歉意。這是一個網格控件。我需要對任何拋出的數據類型進行排序和過濾,只要它是IComparable即可。 – 2010-01-21 17:46:28

4

我認爲,這個代碼真的應該拋出異常時,它不能想出一個轉換。如果傳入的兩個參數是DateTime.NowColor.Fuschsia,那麼您可以在它們之間做任何有意義的比較,因此您返回的任何值都是錯誤的。這是拋出異常的正確時間的定義。

如果您絕對需要避免異常,則無法按照任意類型進行操作。每種類型都有自己的規則,可以解析哪些值,轉換器無法事先告知。 (也就是說,正如您注意到的,它知道您有時可以將string轉換爲DateTime,但它並不旨在知道「2010年1月1日」是有效的DateTime而「Fred」不是。)

+0

在這種情況下,輸入是DateTime和Color是完全有效的。顯然,作爲它們的原始數據類型,它們是無法比較的。這就是爲什麼我想檢查它們是否可以轉換,以便進行有用的比較。我只是想檢查,我不希望這是一個錯誤的情況。 – 2010-01-21 19:05:30

+0

那麼你想要的只是一種方法來檢查轉換是否會成功,而沒有實際進行轉換?這對於任意類型是不可能的。 'TypeConverter'旨在嘗試轉換並在失敗時拋出異常。 你可以檢查你知道如何處理的常見類型,比如'DateTime'和'Double'等,並調用它們的'TryParse'方法。但是,這隻適用於您檢查的類型。 – Auraseer 2010-01-21 20:00:55

5

如果這是不可能把它寫沒有異常,則可以通過重構成像這樣的方法分離出有問題的代碼:

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; 
    } 
} 

在理想情況下應在空類型被傳遞作爲輸出參數,以便null表示未定義的值(因爲它無法進行轉換)而不是默認值(即0表示int)