2010-01-21 24 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)