2010-06-02 2 views
155

Я пытаюсь создать общее расширение, которое использует «TryParse», чтобы проверить, является ли строка данного типа:Generic TryParse

public static bool Is<T>(this string input) 
{ 
    T notUsed; 
    return T.TryParse(input, out notUsed); 
} 

это не будет компилироваться, как он не может разрешить символ «TryParse»

Как я понимаю, «TryParse» не является частью какого-либо интерфейса.

Возможно ли это вообще сделать?

Update:

Используя ответы ниже я придумал:

public static bool Is<T>(this string input) 
{ 
    try 
    { 
     TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input); 
    } 
    catch 
    { 
     return false; 
    } 

    return true; 
} 

Она работает довольно хорошо, но я думаю, что использование исключений таким образом не чувствует себя хорошо для меня.

Update2:

Модифицированный передать тип, а не использовать дженерики:

public static bool Is(this string input, Type targetType) 
{ 
    try 
    { 
     TypeDescriptor.GetConverter(targetType).ConvertFromString(input); 
     return true; 
    } 
    catch 
    { 
     return false; 
    } 
} 
+0

я думаю, что в этом общем случае вам просто придется иметь дело с клуджем исключения. вы можете добавить случаи для проверки таких вещей, как ints или double, а затем использовать определенные методы TryParse, но вам все равно придется отказаться от этого, чтобы поймать другие типы. – luke

+1

Использование генератора не требуется. Просто введите Type в качестве параметра. public static bool Is (этот ввод строки, тип targetType). Этот способ вызова выглядит немного красивее: x.Is (typeof (int)) -VS- x.Is () – mikesigs

+1

На конвертере есть метод IsValid, чтобы вы могли проверить, будут ли проблемы с преобразованием. Я использовал метод ниже и, кажется, работает нормально. 'protected Boolean TryParse (значение объекта, вне результата T) { результат = по умолчанию (T); var convertor = TypeDescriptor.GetConverter (typeof (T)); if (convertor == null ||! Convertor.IsValid (value)) {return false; } result = (T) convertor.ConvertFrom (значение); return true; } ' – CastroXXL

ответ

144

Вы должны использовать TypeDescriptor класс:

public static T Convert<T>(this string input) 
{ 
    try 
    { 
     var converter = TypeDescriptor.GetConverter(typeof(T)); 
     if(converter != null) 
     { 
      // Cast ConvertFromString(string text) : object to (T) 
      return (T)converter.ConvertFromString(input); 
     } 
     return default(T); 
    } 
    catch (NotSupportedException) 
    { 
     return default(T); 
    } 
} 
+3

Жаль воскресить, но возвращает GetConverter null? Я думаю, что если бы это произошло, вероятно, исключение должно быть выбрано вместо того, чтобы, по сути, терпеть неудачу и вернуть что-то еще. Когда я попробовал это на своем собственном классе (в котором я не определил typeconverter), я получил конвертер из GetConverter, но затем ConvertFromString бросил исключение NotSupportedException. – user420667

+2

@ user420667, я считаю, что вы должны проверять результат CanConvertFrom (typeof (string)) перед попыткой конвертировать из строки. TypeConverter может не поддерживать преобразование из строки. –

+2

Вы можете добавить ** if (typeof (T) .IsEnum) {return (T) Enum.Parse (typeof (T), input); } ** [как довольно общий ярлык для всех типов Enum] до получения конвертера. Я полагаю, это зависит от того, как часто вы будете делать типы Enum, а не более сложные типы. –

4

Вы не можете сделать это на общих типов.

Что вы можете сделать, так это создать интерфейс ITryParsable и использовать его для пользовательских типов, реализующих этот интерфейс.

Я предполагаю, что вы намерены использовать это с основными типами, такими как int и DateTime. Вы не можете изменить эти типы для реализации новых интерфейсов.

+1

Интересно, будет ли это работать с использованием динамического ключевого слова в .net 4? –

+0

@Pierre: Это не будет работать по умолчанию в C# с ключевым словом 'dynamic', потому что это не будет работать при статической типизации. Вы можете создать свой собственный динамический объект, который может справиться с этим, но он не является значением по умолчанию. – Steven

0

Это вопрос «общих ограничений». Поскольку у вас нет определенного интерфейса, вы застреваете, если не будете следовать рекомендациям предыдущего ответа.

Для документации по этому поводу, проверьте следующую ссылку:

http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx

Он показывает, как использовать эти ограничения и должны дать вам некоторые подсказки.

1

Как вы сказали, TryParse не является частью интерфейса. Он также не является членом какого-либо базового класса, так как на самом деле static и static функции не могут быть virtual. Таким образом, компилятор не может гарантировать, что у T есть элемент, который называется TryParse, поэтому это не работает.

Как сказал @Mark, вы можете создать свой собственный интерфейс и использовать пользовательские типы, но вам не повезло со встроенными типами.

2

Когда я хотел сделать почти эту вещь, мне пришлось реализовать ее с трудом, учитывая отражение. Учитывая T, задумайтесь над typeof(T) и ищите метод TryParse или Parse, ссылаясь на него, если вы его нашли.

+0

Это то, что я собирался предложить. –

3

Как насчет чего-то подобного?

http://madskristensen.net/post/Universal-data-type-checker.aspx (Archive)

/// <summary> 
/// Checks the specified value to see if it can be 
/// converted into the specified type. 
/// <remarks> 
/// The method supports all the primitive types of the CLR 
/// such as int, boolean, double, guid etc. as well as other 
/// simple types like Color and Unit and custom enum types. 
/// </remarks> 
/// </summary> 
/// <param name="value">The value to check.</param> 
/// <param name="type">The type that the value will be checked against.</param> 
/// <returns>True if the value can convert to the given type, otherwise false. </returns> 
public static bool CanConvert(string value, Type type) 
{ 
    if (string.IsNullOrEmpty(value) || type == null) return false; 
    System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(type); 
    if (conv.CanConvertFrom(typeof(string))) 
    { 
     try 
     { 
      conv.ConvertFrom(value); 
      return true; 
     } 
     catch 
     { 
     } 
    } 
    return false; 
    } 

Это может быть преобразован в общий метод довольно легко.

public static T Is<T>(this string input) 
{ 
    if (string.IsNullOrEmpty(value)) return false; 
    var conv = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)); 

    if (conv.CanConvertFrom(typeof(string))) 
    { 
     try 
     { 
      conv.ConvertFrom(value); 
      return true; 
     } 
     catch 
     { 
     } 
    } 
    return false; 
} 
+0

Неважно, возвращаете ли вы значение true из блока try или возвращаете false из блока catch? Я полагаю, что нет, но я все еще думаю, что использование исключений таким образом кажется неправильным для меня ... –

+3

Неважно, возвращаетесь ли вы из блока catch, это то же самое. Кстати. Обычно плохо иметь общую схему catch: 'catch {}'. Однако в этом случае альтернативы нет, потому что .NET BaseNumberConverter выдает базовый класс «Исключение» в случае ошибки преобразования. Это очень неудачно. На самом деле все еще довольно много мест, где был сброшен этот базовый тип. Надеюсь, Microsoft исправит их в будущей версии фреймворка. – Steven

+0

Спасибо Стивен, не мог бы сказать это лучше. – Bob

14

Если вы настроены на использование TryParse, вы можете использовать отражение и сделать это следующим образом:

public static bool Is<T>(this string input) 
{ 
    var type = typeof (T); 
    var temp = default(T); 
    var method = type.GetMethod(
     "TryParse", 
     new[] 
      { 
       typeof (string), 
       Type.GetType(string.Format("{0}&", type.FullName)) 
      }); 
    return (bool) method.Invoke(null, new object[] {input, temp}); 
} 
+0

Это очень круто, и он избавляется от исключений, которые мне все равно не нравятся. Тем не менее, немного запутанный. –

+5

Хорошее решение, но любой ответ, отражающий отражение (особенно в утилите, который может быть легко вызван из внутреннего цикла), нуждается в отказе от производительности. См. Http://stackoverflow.com/questions/25458/how-costly-is-net-reflection –

+0

Вздох. Таким образом, выбор: (1) использовать исключения для управления потоком кода, (2) использовать отражение с его скоростью. Я согласен с @PiersMyers - ни один из них не идеален. Хорошо, что они оба работают. :) –

0

Заимствованные из http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx

при следовании по этой ссылке: How to invoke static method in C#4.0 with dynamic type?

using System; 
using System.Collections.Generic; 
using System.Dynamic; 
using System.Linq; 
using System.Reflection; 

namespace Utils 
{ 
    public class StaticMembersDynamicWrapper : DynamicObject 
    { 
     private Type _type; 

     public StaticMembersDynamicWrapper(Type type) { _type = type; } 

     // Handle static methods 
     public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 
     { 
     var methods = _type 
      .GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public) 
      .Where(methodInfo => methodInfo.Name == binder.Name); 

     var method = methods.FirstOrDefault(); 
     if (method != null) 
     { 
      result = method.Invoke(null, args); 
      return true; 
     } 

     result = null; 
     return false; 
     } 
    } 

    public static class StaticMembersDynamicWrapperExtensions 
    { 
     static Dictionary<Type, DynamicObject> cache = 
     new Dictionary<Type, DynamicObject> 
     { 
      {typeof(double), new StaticMembersDynamicWrapper(typeof(double))}, 
      {typeof(float), new StaticMembersDynamicWrapper(typeof(float))}, 
      {typeof(uint), new StaticMembersDynamicWrapper(typeof(uint))}, 
      {typeof(int), new StaticMembersDynamicWrapper(typeof(int))}, 
      {typeof(sbyte), new StaticMembersDynamicWrapper(typeof(sbyte))} 
     }; 

     /// <summary> 
     /// Allows access to static fields, properties, and methods, resolved at run-time. 
     /// </summary> 
     public static dynamic StaticMembers(this Type type) 
     { 
     DynamicObject retVal; 
     if (!cache.TryGetValue(type, out retVal)) 
      return new StaticMembersDynamicWrapper(type); 

     return retVal; 
     } 
    } 
} 

И используйте его следующим образом:

public static T? ParseNumeric<T>(this string str, bool throws = true) 
    where T : struct 
    { 
    var statics = typeof(T).StaticMembers(); 

    if (throws) return statics.Parse(str); 

    T retval; 
    if (!statics.TryParse(str, out retval)) return null; 

    return retval; 
    } 
66

Недавно мне также потребовался общий TryParse. Вот что я придумал;

public static T? TryParse<T>(string value, TryParseHandler<T> handler) where T : struct 
{ 
    if (String.IsNullOrEmpty(value)) 
     return null; 
    T result; 
    if (handler(value, out result)) 
     return result; 
    Trace.TraceWarning("Invalid value '{0}'", value); 
    return null; 
} 

public delegate bool TryParseHandler<T>(string value, out T result); 

Тогда это просто вопрос вызова таким образом:

var value = TryParse<int>("123", int.TryParse); 
var value2 = TryParse<decimal>("123.123", decimal.TryParse); 
+3

Спустя несколько месяцев я снова столкнулся с этим сообщением и заметил, что метод не может вывести 'T' из обработчика, и мы должны явно указать' T', когда мы его назовем. Мне любопытно, почему он не может вывести 'T'? –

+0

Другими словами, почему мы не можем использовать 'TryParse (« 42 », Int32.TryParse)' –

+18

Зачем вам нужна эта функция? Если вы знаете, какую функцию вызывать для разбора значения, почему бы просто не назвать ее напрямую? Он уже знает правильный тип ввода и нет необходимости в generics.This решение не будет работать для типов без TryParseHandler. – xxbbcc

0

мне удалось получить то, что работает, как этот

var result = "44".TryParse<int>(); 

    Console.WriteLine("type={0}, value={1}, valid={2}",   
    result.Value.GetType(), result.Value, result.IsValid); 

Вот мой код

public static class TryParseGeneric 
    { 
     //extend int 
     public static dynamic TryParse<T>(this string input) 
     {  
      dynamic runner = new StaticMembersDynamicWrapper(typeof(T)); 

      T value; 
      bool isValid = runner.TryParse(input, out value); 
      return new { IsValid = isValid, Value = value }; 
     } 
    } 


    public class StaticMembersDynamicWrapper : DynamicObject 
    { 
     private readonly Type _type; 
     public StaticMembersDynamicWrapper(Type type) { _type = type; } 

     // Handle static properties 
     public override bool TryGetMember(GetMemberBinder binder, out object result) 
     { 
      PropertyInfo prop = _type.GetProperty(binder.Name, BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public); 
      if (prop == null) 
      { 
       result = null; 
       return false; 
      } 

      result = prop.GetValue(null, null); 
      return true; 
     } 

     // Handle static methods 
     public override bool TryInvokeMember(InvokeMemberBinder binder, object [] args, out object result) 
     { 
      var methods = _type 
      .GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public).Where(methodInfo => methodInfo.Name == binder.Name); 

      var method = methods.FirstOrDefault(); 

      if (method == null) 
      { 
       result = null; 

       return false; 
      } 

      result = method.Invoke(null, args); 

      return true; 
     } 
    } 

StaticMembersDynamicWrapper адаптируется ed от Дэвида Эббо article (он выбрасывал исключение AmbiguousMatchException)

24

Использование try/catch для управления потоком - ужасная политика. Выброс исключения приводит к отставаниям производительности во время работы среды выполнения исключения. Вместо этого проверьте данные перед преобразованием.

var attemptedValue = "asdfasdsd"; 
var type = typeof(int); 
var converter = TypeDescriptor.GetConverter(type); 
if (converter != null && converter.IsValid(attemptedValue)) 
    return converter.ConvertFromString(attemptedValue); 
else 
    return Activator.CreateInstance(type); 
+2

Я получаю сообщение Resharper о том, что 'converter! = Null' всегда истинно, поэтому его можно удалить из кода. – ErikE

+2

@ErikE Я не всегда доверяю этим предупреждениям ReSharper. Часто они не могут видеть, что происходит во время выполнения. – ProfK

+0

@ProfK MSDN не говорит, что он может вернуть null https://msdn.microsoft.com/en-us/library/ewtxwhzx.aspx – danio

7

Это использует статический конструктор для каждого родового типа, поэтому он имеет только, чтобы сделать дорогую работу в первый раз, вы называете его по данному типу. Он обрабатывает все типы в пространстве имен систем, которые имеют методы TryParse. Он также работает с нулевыми версиями каждого из них (которые являются структурами), за исключением перечислений.

public static bool TryParse<t>(this string Value, out t result) 
    { 
     return TryParser<t>.TryParse(Value.SafeTrim(), out result); 
    } 
    private delegate bool TryParseDelegate<t>(string value, out t result); 
    private static class TryParser<T> 
    { 
     private static TryParseDelegate<T> parser; 
     // Static constructor: 
     static TryParser() 
     { 
      Type t = typeof(T); 
      if (t.IsEnum) 
       AssignClass<T>(GetEnumTryParse<T>()); 
      else if (t == typeof(bool) || t == typeof(bool?)) 
       AssignStruct<bool>(bool.TryParse); 
      else if (t == typeof(byte) || t == typeof(byte?)) 
       AssignStruct<byte>(byte.TryParse); 
      else if (t == typeof(short) || t == typeof(short?)) 
       AssignStruct<short>(short.TryParse); 
      else if (t == typeof(char) || t == typeof(char?)) 
       AssignStruct<char>(char.TryParse); 
      else if (t == typeof(int) || t == typeof(int?)) 
       AssignStruct<int>(int.TryParse); 
      else if (t == typeof(long) || t == typeof(long?)) 
       AssignStruct<long>(long.TryParse); 
      else if (t == typeof(sbyte) || t == typeof(sbyte?)) 
       AssignStruct<sbyte>(sbyte.TryParse); 
      else if (t == typeof(ushort) || t == typeof(ushort?)) 
       AssignStruct<ushort>(ushort.TryParse); 
      else if (t == typeof(uint) || t == typeof(uint?)) 
       AssignStruct<uint>(uint.TryParse); 
      else if (t == typeof(ulong) || t == typeof(ulong?)) 
       AssignStruct<ulong>(ulong.TryParse); 
      else if (t == typeof(decimal) || t == typeof(decimal?)) 
       AssignStruct<decimal>(decimal.TryParse); 
      else if (t == typeof(float) || t == typeof(float?)) 
       AssignStruct<float>(float.TryParse); 
      else if (t == typeof(double) || t == typeof(double?)) 
       AssignStruct<double>(double.TryParse); 
      else if (t == typeof(DateTime) || t == typeof(DateTime?)) 
       AssignStruct<DateTime>(DateTime.TryParse); 
      else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?)) 
       AssignStruct<TimeSpan>(TimeSpan.TryParse); 
      else if (t == typeof(Guid) || t == typeof(Guid?)) 
       AssignStruct<Guid>(Guid.TryParse); 
      else if (t == typeof(Version)) 
       AssignClass<Version>(Version.TryParse); 
     } 
     private static void AssignStruct<t>(TryParseDelegate<t> del) 
      where t: struct 
     { 
      TryParser<t>.parser = del; 
      if (typeof(t).IsGenericType 
       && typeof(t).GetGenericTypeDefinition() == typeof(Nullable<>)) 
      { 
       return; 
      } 
      AssignClass<t?>(TryParseNullable<t>); 
     } 
     private static void AssignClass<t>(TryParseDelegate<t> del) 
     { 
      TryParser<t>.parser = del; 
     } 
     public static bool TryParse(string Value, out T Result) 
     { 
      if (parser == null) 
      { 
       Result = default(T); 
       return false; 
      } 
      return parser(Value, out Result); 
     } 
    } 

    private static bool TryParseEnum<t>(this string Value, out t result) 
    { 
     try 
     { 
      object temp = Enum.Parse(typeof(t), Value, true); 
      if (temp is t) 
      { 
       result = (t)temp; 
       return true; 
      } 
     } 
     catch 
     { 
     } 
     result = default(t); 
     return false; 
    } 
    private static MethodInfo EnumTryParseMethod; 
    private static TryParseDelegate<t> GetEnumTryParse<t>() 
    { 
     Type type = typeof(t); 

     if (EnumTryParseMethod == null) 
     { 
      var methods = typeof(Enum).GetMethods(
       BindingFlags.Public | BindingFlags.Static); 
      foreach (var method in methods) 
       if (method.Name == "TryParse" 
        && method.IsGenericMethodDefinition 
        && method.GetParameters().Length == 2 
        && method.GetParameters()[0].ParameterType == typeof(string)) 
       { 
        EnumTryParseMethod = method; 
        break; 
       } 
     } 
     var result = Delegate.CreateDelegate(
      typeof(TryParseDelegate<t>), 
      EnumTryParseMethod.MakeGenericMethod(type), false) 
      as TryParseDelegate<t>; 
     if (result == null) 
      return TryParseEnum<t>; 
     else 
      return result; 
    } 

    private static bool TryParseNullable<t>(string Value, out t? Result) 
     where t: struct 
    { 
     t temp; 
     if (TryParser<t>.TryParse(Value, out temp)) 
     { 
      Result = temp; 
      return true; 
     } 
     else 
     { 
      Result = null; 
      return false; 
     } 
    } 
-1

Версия для получения потомков из XDocument.

public static T Get<T>(XDocument xml, string descendant, T @default) 
{ 
    try 
    { 
     var converter = TypeDescriptor.GetConverter(typeof (T)); 
     if (converter != null) 
     { 
      return (T) converter.ConvertFromString(xml.Descendants(descendant).Single().Value); 
     } 
     return @default; 
    } 
    catch 
    { 
     return @default; 
    } 
} 
0
public static T Get<T>(string val) 
{ 
    return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val); 
} 
2

Это моя попытка. Я сделал это как «упражнение». Я попытался сделать это как аналог использовать как существующий «Convert.ToX()» -онов и т.д. Но этот метод расширения:

public static bool TryParse<T>(this String str, out T parsedValue) 
    { 
     try 
     { 
      parsedValue = (T)Convert.ChangeType(str, typeof(T)); 
      return true; 
     } 

     catch { parsedValue = default(T); return false; } 
    } 
+0

Основным недостатком этого по сравнению с 'TypeConverter.ConvertFrom()' является то, что класс * source * должен обеспечивать преобразование типа, что обычно означает, что вы не можете поддерживать преобразование в пользовательские типы. –

2

Совсем немного опоздал на вечеринку, но вот что Я придумал. Никаких исключений, одноразовых (для каждого типа) отражения.

public static class Extensions { 
    public static T? ParseAs<T>(this string str) where T : struct { 
     T val; 
     return GenericHelper<T>.TryParse(str, out val) ? val : default(T?); 
    } 
    public static T ParseAs<T>(this string str, T defaultVal) { 
     T val; 
     return GenericHelper<T>.TryParse(str, out val) ? val : defaultVal; 
    } 

    private static class GenericHelper<T> { 
     public delegate bool TryParseFunc(string str, out T result); 

     private static TryParseFunc tryParse; 
     public static TryParseFunc TryParse { 
      get { 
       if (tryParse == null) 
        tryParse = Delegate.CreateDelegate(
         typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc; 
       return tryParse; 
      } 
     } 
    } 
} 

Дополнительный класс необходим, поскольку методы расширения не разрешены внутри общих классов. Это позволяет простое использование, как показано ниже, и отражает только отражение при первом использовании типа.

"5643".ParseAs<int>() 
3

Вдохновленный раствором, размещенных здесь Charlie Brown, я создал общий TryParse с помощью отражения, что при необходимости выводит разобранный значение:

/// <summary> 
/// Tries to convert the specified string representation of a logical value to 
/// its type T equivalent. A return value indicates whether the conversion 
/// succeeded or failed. 
/// </summary> 
/// <typeparam name="T">The type to try and convert to.</typeparam> 
/// <param name="value">A string containing the value to try and convert.</param> 
/// <param name="result">If the conversion was successful, the converted value of type T.</param> 
/// <returns>If value was converted successfully, true; otherwise false.</returns> 
public static bool TryParse<T>(string value, out T result) where T : struct { 
    var tryParseMethod = typeof(T).GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, null, new [] { typeof(string), typeof(T).MakeByRefType() }, null); 
    var parameters = new object[] { value, null }; 

    var retVal = (bool)tryParseMethod.Invoke(null, parameters); 

    result = (T)parameters[1]; 
    return retVal; 
} 

/// <summary> 
/// Tries to convert the specified string representation of a logical value to 
/// its type T equivalent. A return value indicates whether the conversion 
/// succeeded or failed. 
/// </summary> 
/// <typeparam name="T">The type to try and convert to.</typeparam> 
/// <param name="value">A string containing the value to try and convert.</param> 
/// <returns>If value was converted successfully, true; otherwise false.</returns> 
public static bool TryParse<T>(string value) where T : struct { 
    T throwaway; 
    var retVal = TryParse(value, out throwaway); 
    return retVal; 
} 

Это можно назвать так:

string input = "123"; 
decimal myDecimal; 

bool myIntSuccess = TryParse<int>(input); 
bool myDecimalSuccess = TryParse<decimal>(input, out myDecimal); 

Обновление:
Также благодаря решению YotaXP, которое мне очень нравится, я создал версия, которая не использует методы расширения, но до сих пор синглтон, сводя к минимуму необходимость сделать отражение:

/// <summary> 
/// Provides some extra parsing functionality for value types. 
/// </summary> 
/// <typeparam name="T">The value type T to operate on.</typeparam> 
public static class TryParseHelper<T> where T : struct { 
    private delegate bool TryParseFunc(string str, out T result); 

    private static TryParseFunc tryParseFuncCached; 

    private static TryParseFunc tryParseCached { 
     get { 
      return tryParseFuncCached ?? (tryParseFuncCached = Delegate.CreateDelegate(typeof(TryParseFunc), typeof(T), "TryParse") as TryParseFunc); 
     } 
    } 

    /// <summary> 
    /// Tries to convert the specified string representation of a logical value to 
    /// its type T equivalent. A return value indicates whether the conversion 
    /// succeeded or failed. 
    /// </summary> 
    /// <param name="value">A string containing the value to try and convert.</param> 
    /// <param name="result">If the conversion was successful, the converted value of type T.</param> 
    /// <returns>If value was converted successfully, true; otherwise false.</returns> 
    public static bool TryParse(string value, out T result) { 
     return tryParseCached(value, out result); 
    } 

    /// <summary> 
    /// Tries to convert the specified string representation of a logical value to 
    /// its type T equivalent. A return value indicates whether the conversion 
    /// succeeded or failed. 
    /// </summary> 
    /// <param name="value">A string containing the value to try and convert.</param> 
    /// <returns>If value was converted successfully, true; otherwise false.</returns> 
    public static bool TryParse(string value) { 
     T throwaway; 
     return TryParse(value, out throwaway); 
    } 
} 

Зов это следующим образом:

string input = "987"; 
decimal myDecimal; 

bool myIntSuccess = TryParseHelper<int>.TryParse(input); 
bool myDecimalSuccess = TryParseHelper<decimal>.TryParse(input, out myDecimal); 
1

Вот еще один вариант.

Я написал класс, который позволяет легко зарегистрировать любое количество обработчиков TryParse. Это позволяет мне сделать это:

var tp = new TryParser(); 

tp.Register<int>(int.TryParse); 
tp.Register<decimal>(decimal.TryParse); 
tp.Register<double>(double.TryParse); 

int x; 
if (tp.TryParse("42", out x)) 
{ 
    Console.WriteLine(x); 
}; 

я 42 выводимой на консоль.

Класса:

public class TryParser 
{ 
    public delegate bool TryParseDelegate<T>(string s, out T result); 

    private Dictionary<Type, Delegate> _tryParsers = new Dictionary<Type, Delegate>(); 

    public void Register<T>(TryParseDelegate<T> d) 
    { 
     _tryParsers[typeof(T)] = d; 
    } 

    public bool Deregister<T>() 
    { 
     return _tryParsers.Remove(typeof(T)); 
    } 

    public bool TryParse<T>(string s, out T result) 
    { 
     if (!_tryParsers.ContainsKey(typeof(T))) 
     { 
      throw new ArgumentException("Does not contain parser for " + typeof(T).FullName + "."); 
     } 
     var d = (TryParseDelegate<T>)_tryParsers[typeof(T)]; 
     return d(s, out result); 
    } 
} 
+0

Мне это нравится, но как бы вы это сделали без дженериков. Разумеется, использование случая является отражением. – Sinaesthetic

+0

Я добавил перегруженный метод, который делает некоторый рефлексивный хакер. Если есть более элегантный способ обратиться к нему, я все глаза lol https://gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94 – Sinaesthetic