Я работаю над элементом управления, который может принимать несколько различных типов данных (все, что реализует IComparable).
Мне нужно сравнить их с другой переданной переменной.
Если основным типом данных является DateTime, и мне передана строка, мне нужно
- попытка преобразовать строку в DateTime для сравнения дат.
- если строка не может быть преобразована в DateTime, тогда выполните сравнение строки.
Так что мне нужен общий способ попытаться преобразовать любой тип в любой тип. Достаточно просто, .Net предоставляет нам класс TypeConverter .
Теперь лучшее, что я могу сделать, чтобы определить, может ли String быть преобразована в 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 работает только в том случае, если возможно преобразование между этими типами, и не учитывает фактические данные для преобразования. Метод IsValid также бесполезен.
Есть идеи?
EDIT
Я не могу использовать AS и IS. Я не знаю ни одного типа данных во время компиляции. Так что я не знаю, что и как и * !!! 1044 *
EDIT
Хорошо, прибил ублюдка. Это не так аккуратно, как Марк Гравеллс, но это работает (я надеюсь). Спасибо за вдохновение, Марк. Я поработаю над тем, чтобы привести его в порядок, когда у меня будет время, но у меня есть небольшой стек исправлений, с которыми мне придется поработать.
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 () } );
}
}
}