通用的TryParse扩展方法

14

代码来源于这里

我想听听一些专家对这个扩展方法的意见。我打算使用它,但想了解可能会遇到的任何已知问题。

对于原始类型,我是否最好使用TryParse方法?

public static T? TryParse<T>(this object obj) where T : struct
        {
            if (obj == null) return null;

            T? result = null;
            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
            if (converter != null)
            {
                try
                {
                    string str = obj.ToString();
                    result = (T)converter.ConvertFromString(str);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            return result;
        }

5
正如其他回答所说,不要使用“throw ex”。你很少或几乎从不需要这样做。只需使用“throw”即可。 - Rex M
4
如果你抛出异常,那么就失去了“尝试(Try)”的意义。 - Marc Gravell
5个回答

28

TryParse模式最好遵循标准模式,这样可以与非结构体一起使用:

public static bool TryParse<T>(string s, out T value) {
    TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
    try {
        value = (T) converter.ConvertFromString(s);
        return true;
    } catch {
        value = default(T);
        return false;
    }
}

请注意,我在这里接受了一个string,因为这通常是我们所说的TryParse的意思;否则,Convert.ChangeType可能更合适。

我认为这没有必要成为扩展方法(如问题示例中的this),而且向object添加过多的扩展方法肯定是不可取的。


4
以下扩展可能对您有用。它们适用于具有Parse或TryParse方法的任何类型...
它们来自我的扩展库,位于此处: http://www.codeproject.com/KB/dotnet/MBGExtensionsLibrary.aspx 尽管该项目可能有点过时...我将不得不在某个时候更新它 :-D
希望这可以帮助您!
public static class StringExtensions
    {
        public static TOut ParseOrDefault<TOut>(this string input)
        {
            return input.ParseOrDefault(default(TOut));
        }
        public static TOut ParseOrDefault<TOut>(this string input, TOut defaultValue)
        {
            Type type = typeof(TOut);
            MethodInfo parseMethod = type.GetMethod("Parse", new Type[] { typeof(string) });

            if (parseMethod != null)
            {
                var value = parseMethod.Invoke(null, new string[] { input });
                return (value is TOut ? (TOut)value : defaultValue);
            }
            else { return defaultValue; }
        }
        public static bool TryParseOrDefault<TOut>(this string input, out TOut output)
        {
            return input.TryParseOrDefault(out output, default(TOut));
        }
        public static bool TryParseOrDefault<TOut>(this string input, out TOut output, TOut defaultValue)
        {
            output = defaultValue;

            Type type = typeof(TOut);
            MethodInfo parseMethod = type.GetMethod(
                "TryParse",
                new Type[] { typeof(string), typeof(TOut).MakeByRefType() });

            if (parseMethod != null)
            {
                object[] parameters = new object[] { input, output };
                var value = parseMethod.Invoke(null, parameters);

                if (value is bool)
                {
                    bool successful = (bool)value;
                    if (successful)
                    {
                        output = (TOut)parameters[1];
                        return true;
                    }
                }
            }
            return false;
        }
    }

哎呀...我没看到这篇文章有多久了!哈哈。对不起啊。 - Matt

3
泛型最有用的是当你想要改变方法或类的公共契约,而方法或类的内部并不关心(或者很少关心)变化的类型时。
一些例子:
List 是一个可以放东西的集合,而在内部,类并不关心那个类型是什么。
T System.Linq.Enumerable.First(IEnumerable source) 从一堆元素中返回第一个元素。这个方法不需要在内部知道那个类型才能完成工作。
相比之下,解析方法必须根据结果的类型改变其行为。在提供的方法中,有一个“策略”将行为推向其他方法,但这种选择会产生运行时成本。
另一种选择是让调用者(必须知道类型,否则他们无法调用泛型方法),选择转换器。这个选择可以在设计时间或编译时间做出,因此不会产生任何运行时成本。
附注:请不要使用“重新抛出所有”习惯用法。它只会重置调用堆栈,你永远不想这样做。
catch (Exception ex)
{
  throw ex;
}

使用这种通用方法的动机是减少调用客户端代码与使用原始解析器之间的差异。您说得对,客户端知道运行时类型,但他们也必须处理异常。此外,您能否给我提供有关重置堆栈跟踪的更多信息的链接?谢谢。 - Anton P
而且,你可以让客户端传递解析器给你(如果没有其他方法,可以使用Func<T, U>),而不是类型 - 这样可以避免反射。 - Amy B

3

1

它使用反射,因此如果性能是一个问题,可能会很慢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接