通用 TryParse

Posted

技术标签:

【中文标题】通用 TryParse【英文标题】:Generic TryParse 【发布时间】:2010-06-02 21:16:30 【问题描述】:

我正在尝试创建一个使用“TryParse”来检查字符串是否为给定类型的通用扩展:

public static bool Is<T>(this string input)

    T notUsed;
    return T.TryParse(input, out notUsed);

这将无法编译,因为它无法解析符号“TryParse”

据我了解,“TryParse”不是任何界面的一部分。

这有可能吗?

更新:

使用以下我想出的答案:

public static bool Is<T>(this string input)

    try
    
        TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input);
    
    catch
    
        return false;
    

    return true;

它工作得很好,但我认为以这种方式使用异常对我来说并不合适。

更新2:

修改为传递类型而不是使用泛型:

public static bool Is(this string input, Type targetType)

    try
    
        TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
        return true;
    
    catch
    
        return false;
    

【问题讨论】:

我认为在这种一般情况下,您只需要处理异常 kludge。您可以添加案例来检查整数或双精度,然后使用特定的 TryParse 方法,但您仍然必须依靠它来捕获其他类型。 没有必要使用泛型。只需将 Type 作为参数传入即可。 public static bool Is(this string input, Type targetType) 。这样称呼它看起来更漂亮:x.Is(typeof(int)) -VS- x.Is() 转换器上有一个 IsValid 方法供您检查转换是否会出现问题。我使用了下面的方法,似乎工作正常。 protected Boolean TryParse&lt;T&gt;(Object value, out T result) result = default(T); var convertor = TypeDescriptor.GetConverter(typeof(T)); if (convertor == null || !convertor.IsValid(value)) return false; result = (T)convertor.ConvertFrom(value); return true; @CastroXXL 感谢您对这个问题感兴趣,但是您的方法不太适用,因为我想检查字符串值是否属于某种类型而不是对象,尽管您的方法会对对象类型很有用(但必须将 ConvertFrom(value) 方法包装在 try-catch 块中以捕获异常。 您应该检查 if (targetType == null),因为在您的代码中第一次使用它可能会抛出异常,但该异常会被您的 catch 吞没。 【参考方案1】:

您应该使用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);
    

【讨论】:

抱歉复活了,但是GetConverter返回null吗?我认为如果确实如此,那么可能应该抛出一个异常,而不是基本上默默地​​失败并返回其他东西。当我在自己的类(我没有定义类型转换器)上尝试它时,我从 GetConverter 获得了一个转换器,但随后 ConvertFromString 抛出了 NotSupportedException。 @user420667,我相信你应该在尝试从字符串转换之前检查 CanConvertFrom(typeof(string)) 的结果。 TypeConverter 可能不支持从字符串转换。 你可以添加 if (typeof(T).IsEnum) return (T)Enum.Parse(typeof(T), input); [作为所有 Enum 类型的相当通用的快捷方式] 在获取转换器之前。我想这取决于您使用 Enum 类型而不是更复杂的类型的频率。 我不明白为什么这被标记为答案并在它没有实现所要求的内容时受到如此多的支持:一个通用的 TryParse。 TryParse 方法的主要目的是,它们在尝试执行解析时不会抛出异常,并且在解析失败并且此解决方案无法提供这一点时对性能的影响要小得多。 一个问题是,如果 T 是一个 int 并且输入大于 int.MaxValue,它将抛出一个带有 System.OverFlowException 的 System.Exception 作为内部异常。所以如果你期待一个溢出异常,除非你询问抛出的异常,否则你不会得到它。原因是 ConvertFromString 抛出一个 OverflowException,然后强制转换为 T 抛出一个 System.Exception。【参考方案2】:

我最近还需要一个通用的 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);

【讨论】:

几个月后再次看到这篇文章,并在再次使用它时注意到该方法无法从处理程序中推断出T,我们必须在调用它时显式指定T。我很好奇,为什么不能推断T 为什么要使用这个功能?如果您知道要调用哪个函数来解析值,为什么不直接调用它呢?它已经知道正确的输入类型并且不需要泛型。此解决方案不适用于没有 TryParseHandler 的类型。 @xxbbcc:我想使用这个函数,因为 TryParse 返回一个布尔值,指示解析是否成功。它通过输出参数返回您的解析值。有时我只想做这样的事情SomeMethod(TryParse&lt;int&gt;(DollarTextbox.Text, int.TryParse)) 而不创建输出变量来捕获来自int.TryParse 的结果。不过,我同意 Nick 关于让函数推断类型的观点。 非常有效的方法。强烈推荐。 我会推荐一个默认值作为第三个参数。这解决了无法推断 T 的问题。此外,如果字符串值无效,它允许人们决定他们想要什么值。例如,-1 可能意味着无效。公共静态 T TryParse(字符串值,TryParseHandler 处理程序,T defaultValue)【参考方案3】:

使用 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);

【讨论】:

我收到 Resharper 通知,converter != null 始终为真,因此可以将其从代码中删除。 @ErikE 我并不总是相信那些 ReSharper 警告。他们通常看不到运行时发生了什么。 @ProfK MSDN 并没有说它可以返回 null msdn.microsoft.com/en-us/library/ewtxwhzx.aspx @danio 我只是在分享我对此类 R# 警告的一般经验。我当然没有暗示在这种情况下这是错误的。【参考方案4】:

如果您打算使用 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);

【讨论】:

这很酷,而且它消除了我不喜欢的异常。不过还是有点绕。 不错的解决方案,但任何涉及反射的答案(尤其是在可以轻松从内部循环调用的实用方法中)都需要关于性能的免责声明。见:***.com/questions/25458/how-costly-is-net-reflection 叹息。所以选择是(1)使用异常进行代码流控制,(2)使用反射及其速度成本。我同意@PiersMyers - 这两种选择都不理想。好在他们都工作。 :) 我认为您可以将Type.GetType(string.Format(...)) 替换为type.MakeByRefType() 该方法只需要每个类型反映一次,而不是每个调用一次。如果你把它变成一个带有静态成员变量的泛型类,那么你可以重用第一个反射的输出。【参考方案5】:

这对每个泛型类型都使用了一个静态构造函数,因此它只需要在您第一次在给定类型上调用它时执行昂贵的工作。它处理系统命名空间中具有 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;
        
    

【讨论】:

【参考方案6】:

这样的事情怎么样?

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 bool Is<T>(this string value)
 
    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;

【讨论】:

从 try 块返回 true 还是从 catch 块返回 false 是否重要?我想不是,但我仍然认为以这种方式使用异常对我来说是错误的...... 从catch块返回没关系,都是一样的。顺便提一句。通常有一个通用的 catch 子句是不好的:catch 。但是,在这种情况下没有其他选择,因为 .NET BaseNumberConverter 在发生转换错误的情况下会抛出 Exception 基类。这是非常不幸的。实际上还是有不少地方抛出了这个基类型。希望微软能在框架的未来版本中修复这些问题。 谢谢史蒂文,再好不过了。 没有使用转换结果:代码是多余的。【参考方案7】:

你不能在一般类型上这样做。

您可以做的是创建一个接口 ItryParsable 并将其用于实现此接口的自定义类型。

我猜你打算将它与intDateTime 等基本类型一起使用。您无法更改这些类型来实现新接口。

【讨论】:

我想知道在 .net 4 中使用 dynamic 关键字是否可行? @Pierre:默认情况下,这在带有 dynamic 关键字的 C# 中不起作用,因为它不适用于静态类型。您可以创建自己的动态对象来处理此问题,但这不是默认设置。【参考方案8】:

聚会有点晚了,但这是我想出的。没有例外,一次性(每种类型)反射。

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>()

【讨论】:

【参考方案9】:

受 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);

【讨论】:

【参考方案10】:

这是另一种选择。

我编写了一个类,可以轻松注册任意数量的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);
    

【讨论】:

我喜欢这个,但是如果没有泛型你会怎么做。用例当然是反射。 我添加了一个重载的方法来做一些反射黑客。如果有更优雅的方式来解决它,我都在看哈哈gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94 您可以从委托中删除泛型,但将其保留在方法签名中,以便获得类型推断。您还可以通过定义静态通用存储来删除按类型键入的字典:private static class Storage&lt;T&gt; public static TryParseDelegate&lt;T&gt; Delegate; ,然后从您的方法中设置它。【参考方案11】:

当我想做几乎完全一样的事情时,我不得不以艰难的方式实现它,考虑到反射。给定T,反映typeof(T) 并寻找TryParseParse 方法,如果找到就调用它。

【讨论】:

这就是我要建议的。【参考方案12】:

这是我的尝试。我把它当作“练习”。我试图使其与现有的“Convert.ToX()”-ones 等类似。但这是扩展方法:

    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; 
    

【讨论】:

这个与TypeConverter.ConvertFrom()相比的主要缺点是 source 类必须提供类型转换,这通常意味着您不能支持转换为自定义类型。跨度> 【参考方案13】:

正如你所说,TryParse 不是接口的一部分。它也不是任何给定基类的成员,因为它实际上是staticstatic 函数不能是virtual。因此,编译器无法确保 T 确实有一个名为 TryParse 的成员,所以这不起作用。

正如@Mark 所说,您可以创建自己的界面并使用自定义类型,但您对内置类型不走运。

【讨论】:

【参考方案14】:
public static class Primitive

    public static DateTime? TryParseExact(string text, string format, IFormatProvider formatProvider = null, DateTimeStyles? style = null)
    
        DateTime result;
        if (DateTime.TryParseExact(text, format, formatProvider, style ?? DateTimeStyles.None, out result))
            return result;
        return null;
    

    public static TResult? TryParse<TResult>(string text) where TResult : struct
    
        TResult result;
        if (Delegates<TResult>.TryParse(text, out result))
            return result;
        return null;
    

    public static bool TryParse<TResult>(string text, out TResult result) => Delegates<TResult>.TryParse(text, out result);

    public static class Delegates<TResult>
    
        private delegate bool TryParseDelegate(string text, out TResult result);

        private static readonly TryParseDelegate _parser = (TryParseDelegate)Delegate.CreateDelegate(typeof(TryParseDelegate), typeof(TResult), "TryParse");

        public static bool TryParse(string text, out TResult result) => _parser(text, out result);
    

【讨论】:

【参考方案15】:

这是一个“通用约束”的问题。因为您没有特定的界面,所以除非您遵循上一个答案的建议,否则您会被卡住。

有关这方面的文档,请查看以下链接:

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

它向您展示了如何使用这些约束,并且应该为您提供更多线索。

【讨论】:

【参考方案16】:

借自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;
  

【讨论】:

【参考方案17】:

我设法得到了像这样工作的东西

    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 改编自 David Ebbo 的 article(它抛出了 AmbiguousMatchException)

【讨论】:

【参考方案18】:
public static T Get<T>(string val)
 
    return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val);

【讨论】:

【参考方案19】:

TypeDescriptor类以TryParse相关的方式使用:

public static bool TryParse<T>(this string input, out T parsedValue)

    parsedValue = default(T);
    try
    
        var converter = TypeDescriptor.GetConverter(typeof(T));
        parsedValue = (T)converter.ConvertFromString(input);
        return true;
    
    catch (NotSupportedException)
    
        return false;
    

【讨论】:

虽然此代码可能会解决问题,including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。【参考方案20】:

使用上面的信息,这就是我开发的。直接转换对象是可能的,否则它会将对象转换为字符串并为所需的对象类型调用 TryParse 方法。

我将遇到的每个方法缓存在字典中,以减少方法获取负载。

可以测试对象是否可以直接转换为目标类型,这样可以进一步减少字符串转换的部分。但我暂时不提。

    /// <summary>
    /// Used to store TryParse converter methods
    /// </summary>
    private static readonly Dictionary<Type, MethodInfo> TypeConverters = new Dictionary<Type, MethodInfo>();

    /// <summary>
    /// Attempt to parse the input object to the output type
    /// </summary>
    /// <typeparam name="T">output type</typeparam>
    /// <param name="obj">input object</param>
    /// <param name="result">output result on success, default(T) on failure</param>
    /// <returns>Success</returns>
    public static bool TryParse<T>([CanBeNull] object obj, out T result)
    
        result = default(T);

        try
        
            switch (obj)
            
                // don't waste time on null objects
                case null: return false;

                // if the object is already of type T, just return the value
                case T val:
                    result = val;
                    return true;
            

            // convert the object into type T via string conversion
            var input = ((obj as string) ?? obj.ToString()).Trim();
            if (string.IsNullOrEmpty(input)) return false;

            var type = typeof (T);
            Debug.WriteLine($"Info: nameof(TryParse)<type.Name>(obj.GetType().Name=\"input\")");

            if (! TypeConverters.TryGetValue(type, out var method))
            
                // get the TryParse method for this type
                method = type.GetMethod("TryParse",
                    new[]
                    
                        typeof (string),
                        Type.GetType($"type.FullName&")
                    );

                if (method is null)
                    Debug.WriteLine($"FAILED: Cannot get method for type.Name.TryParse()");

                // store it so we don't have to do this again
                TypeConverters.Add(type, method);
            

            // have to keep a reference to parameters if you want to get the returned ref value
            var parameters = new object[] input, null;
            if ((bool?) method?.Invoke(null, parameters) == true)
            
                result = (T) parameters[1];
                return true;
                            
        
        catch (Exception ex)
        
            Debug.WriteLine(ex);
        

        return false;
    

【讨论】:

我不得不添加另一个函数来支持枚举。似乎枚举解析需要“where T:struct”属性,我希望它适用于任何可转换的东西。 (可能应该为类型添加一个可转换的属性)。但是,以下一些建议看起来更简单(因此更好)。【参考方案21】:

我在这里汇集了一堆想法,最后得到了一个非常简短的解决方案。

这是一个字符串的扩展方法

enter code here

我使用与数字类型上的 TryParse 方法相同的足迹

    /// <summary>
    /// string.TryParse()
    /// 
    /// This generic extension method will take a string
    ///     make sure it is not null or empty
    ///     make sure it represents some type of number e.g. "123" not "abc"
    ///     It then calls the appropriate converter for the type of T
    /// </summary>
    /// <typeparam name="T">The type of the desired retrunValue e.g. int, float, byte, decimal...</typeparam>
    /// <param name="targetText">The text to be converted</param>
    /// <param name="returnValue">a populated value of the type T or the default(T) value which is likely to be 0</param>
    /// <returns>true if the string was successfully parsed and converted otherwise false</returns>
    /// <example>
    /// float testValue = 0;
    ///  if ( "1234".TryParse<float>( out testValue ) )
    ///  
    ///      doSomethingGood();
    ///  
    ///  else
    ///  
    ///      handleTheBadness();
    ///  
    /// </example>
    public static bool TryParse<T>(this string targetText, out T returnValue )
    
        bool returnStatus = false;

        returnValue = default(T);

        //
        // make sure the string is not null or empty and likely a number...
        // call whatever you like here or just leave it out - I would
        // at least make sure the string was not null or empty  
        //
        if ( ValidatedInputAnyWayYouLike(targetText) )
        

            //
            // try to catch anything that blows up in the conversion process...
            //
            try
            
                var type = typeof(T);
                var converter = TypeDescriptor.GetConverter(type);

                if (converter != null && converter.IsValid(targetText))
                
                    returnValue = (T)converter.ConvertFromString(targetText);
                    returnStatus = true;
                

            
            catch
            
                // just swallow the exception and return the default values for failure
            

        

        return (returnStatus);

    

'''

【讨论】:

浮动测试值 = 0; if ("1234".TryParse(out testValue)) doSomethingGood(); 其他 处理坏处(); 【参考方案22】:

T.TryParse ... 为什么?

我认为拥有这种通用的TryParse 函数没有任何好处。在不同类型之间解析和转换数据的策略太多,可能存在冲突行为。这个函数如何知道在上下文无关的方式中选择哪种策略?

可以调用具有专用 TryParse 函数的类 具有专用 Parse 函数的类可以用 try-catch 和 bool 结果包装 具有运算符重载的类,您将如何让它们处理解析? 类型描述符是使用Convert.ChangeType 内置的。此 API 可在运行时自定义。您的函数是否需要默认行为或允许自定义? 是否应允许任何映射框架尝试为您解析? 您将如何处理上述冲突?

【讨论】:

【参考方案23】:

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

【讨论】:

以上是关于通用 TryParse的主要内容,如果未能解决你的问题,请参考以下文章

怎样写一个通用的JDBC查询方法

通用传递类型列表以用于通用方法[重复]

通用无状态组件 React 的类型?或在打字稿中扩展通用函数接口以具有进一步的通用性?

怎样选择通用计数器?

在通用超类中自动装配通用组件

具有通用特征的结构,这也是通用特征