通用TryParse

Pie*_*ers 191 c# generics tryparse

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

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

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

据我了解,'TryParse'不是任何界面的一部分.

这有可能吗?

更新:

使用下面的答案,我想出了:

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

    return true;
}
Run Code Online (Sandbox Code Playgroud)

它运作得很好,但我认为以这种方式使用异常对我来说感觉不对.

UPDATE2:

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

public static bool Is(this string input, Type targetType)
{
    try
    {
        TypeDescriptor.GetConverter(targetType).ConvertFromString(input);
        return true;
    }
    catch
    {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

luk*_*uke 174

您应该使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

小智 77

我最近还需要一个通用的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);
Run Code Online (Sandbox Code Playgroud)

那么这只是一个如此称呼的问题:

var value = TryParse<int>("123", int.TryParse);
var value2 = TryParse<decimal>("123.123", decimal.TryParse);
Run Code Online (Sandbox Code Playgroud)

  • 你为什么要使用这个功能?如果您知道要调用哪个函数来解析值,为什么不直接调用它?它已经知道正确的输入类型,并且不需要泛型.这个解决方案不适用于没有TryParseHandler的类型. (25认同)
  • 几个月后再次发现这篇文章并注意到再次使用它时该方法无法从处理程序推断出"T",我们在调用它时必须明确指定`T`.我很好奇,为什么不能推断出"T"? (3认同)
  • 非常有效的方法。强烈推荐。 (3认同)
  • 我建议将默认值作为第三个参数.这解决了无法推断T的问题.此外,它允许一个人在字符串值无效时决定他们想要的值.例如,-1可能表示无效.public static T TryParse <T>(字符串值,TryParseHandler <T>处理程序,T defaultValue) (3认同)
  • @xxbbcc:我想使用这个函数,因为TryParse返回一个布尔值,指示解析是否成功.它通过输出参数返回您的解析值.有时我只想做一些像这样的东西`SomeMethod(TryParse <int>(DollarTextbox.Text,int.TryParse))`而不创建输出变量来捕获`int.TryParse`的结果.但是,我确实同意尼克关于让函数推断出类型的看法. (2认同)

Thi*_*tes 31

使用try/catches进行流量控制是一项糟糕的策略.抛出异常会导致性能滞后,而运行时则会遇到异常.而是在转换之前验证数据.

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);
Run Code Online (Sandbox Code Playgroud)

  • @ErikE我并不总是相信那些ReSharper警告.通常他们无法看到运行时会发生什么. (5认同)
  • 我得到一个Resharper,注意`converter!= null`总是正确的,所以它可以从代码中删除. (2认同)

Jos*_*ant 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});
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案,但任何涉及反射的答案(特别是在可以从内循环中轻松调用的实用程序方法)都需要关于性能的免责声明.请参阅:http://stackoverflow.com/questions/25458/how-costly-is-net-reflection (6认同)
  • 该方法每个类型只需要反映一次,而不是每次调用一次.如果你使用静态成员变量创建一个泛型类,那么你可以重用第一个反射的输出. (3认同)

Bry*_*ner 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)


Bob*_*Bob 5

这样的事情怎么样?

http://madskristensen.net/post/Universal-data-type-checker.aspx存档

/// <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;
  }
Run Code Online (Sandbox Code Playgroud)

可以很容易地将其转换为通用方法。

 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 是否从catch块返回都没关系,这是相同的。顺便说一句 通常,拥有通用的catch子句是很糟糕的:`catch {}`。但是,在这种情况下别无选择,因为.NET的BaseNumberConverter会在发生转换错误时抛出Exception基类。这是非常不幸的。实际上,仍然存在很多抛出此基本类型的地方。希望Microsoft在框架的将来版本中修复这些问题。 (3认同)

Yot*_*aXP 5

参加聚会有点晚了,但这就是我的想法。无一例外,一次性(每种类型)反射。

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

需要额外的类,因为泛型类中不允许扩展方法。这允许简单的使用,如下所示,并且仅在第一次使用类型时触发反射。

"5643".ParseAs<int>()
Run Code Online (Sandbox Code Playgroud)