通用TryParse


196

我正在尝试创建一个通用扩展,该扩展使用'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;
    }
}

1
我认为在这种一般情况下,您只需要处理异常纠缠。您可以添加大小写以检查诸如int或doubles之类的内容,然后使用特定的TryParse方法,但是您仍然必须依靠此方法来捕获其他类型。
路加福音

1
泛型的使用是不必要的。只需传入Type作为参数即可。公共静态布尔Is(此字符串输入,输入targetType)。这样调用它看起来更漂亮:x.Is(typeof(int))
VS-

2
转换器上有一个IsValid方法,供您检查转换是否有问题。我使用下面的方法,似乎工作正常。 protected Boolean TryParse<T>(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 2011年

@CastroXXL感谢您对此问题的关注,但是您的方法无法正常工作,因为我想检查字符串值是否为特定类型而不是对象,尽管您的方法对于对象类型很有用(但是有包裹ConvertFrom(value)在一个方法try-catch块捕获的异常。
墩迈尔斯

2
您应该检查(targetType == null),因为在代码中第一次使用它可能会抛出该异常,但是该异常会被捕获吞没。
Nick Strupat 2013年

Answers:


183

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

3
@ user420667,我相信您应该在尝试从字符串转换之前检查CanConvertFrom(typeof(string))的结果。TypeConverter可能不支持从字符串转换。
Reuben Bond

3
您可以添加if(typeof(T).IsEnum){return(T)Enum.Parse(typeof(T),input); } [作为所有Enum类型的相当通用的快捷方式],然后再获取Converter。我想这取决于您执行Enum类型而不是更复杂类型的频率。
Jesse Chisholm 2014年

10
我不明白为什么当它没有实现所要求的内容时,为什么将其标记为答案并被大肆抨击:通用的Try Parse。TryParse方法的主要目的是,在尝试执行解析时它们不会引发异常,并且在解析失败且此解决方案无法提供这种效果时,它们对性能的影响要小得多。
Florin Dumitrescu 2014年

2
有一个问题是,如果T是一个int且输入大于int.MaxValue,它将抛出一个System.Exception w / System.OverFlowException作为内部异常。因此,如果您期望一个OverflowException,除非您询问抛出的Exception,否则您不会得到它。原因是ConvertFromString引发OverflowException,然后强制转换为T引发System.Exception。
Trevor

78

我最近还需要通用的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呢?
Nick Strupat 2012年

25
为什么要使用此功能?如果您知道要调用哪个函数来解析值,为什么不直接调用它呢?它已经知道正确的输入类型,并且不需要泛型。如果没有TryParseHandler,此解决方案将不适用于类型。
xxbbcc

2
@xxbbcc:我想使用此函数,因为TryParse返回一个布尔值,该值指示解析是否成功。它通过输出参数返回您的解析值。有时我只是想做这样的事情,SomeMethod(TryParse<int>(DollarTextbox.Text, int.TryParse))而无需创建输出变量来捕获来自的结果int.TryParse。但是,我确实同意Nick关于让函数推断类型的观点。
Walter Stabosz 2013年

1
非常有效的方法。强烈推荐。
弗拉基米尔·科赞契奇(Fladimir Kocjancic)

3
我建议将默认值作为第三个参数。这解决了无法推断T的问题。同样,它允许在字符串值无效的情况下决定所需的值。例如,-1可能意味着无效。公共静态T TryParse <T>(字符串值,TryParseHandler <T>处理程序,T defaultValue)
Rhyous

33

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

2
我收到了一个converter != null始终保持正确的Resharper通知,因此可以将其从代码中删除。
ErikE 2014年

5
@ErikE我并不总是信任那些ReSharper警告。通常,他们看不到运行时发生的情况。
ProfK 2014年

1
@ProfK MSDN没有说它可以返回null msdn.microsoft.com/en-us/library/ewtxwhzx.aspx
danio

@danio我只是在与一般的R#警告分享我的经验。我当然不是在这种情况下暗示这是错误的。
ProfK

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

这非常酷,并且摆脱了我不喜欢的异常。不过还是有些令人费解。
Piers Myers

6
不错的解决方案,但是任何涉及反射的答案(特别是在可以从内部循环轻松调用的实用方法中)都需要对性能免责。请参阅:stackoverflow.com/questions/25458/how-costly-is-net-reflection
Patrick M

叹。因此,选择是(1)使用异常进行代码流控制,(2)使用反射及其速度成本。我同意@PiersMyers-两种选择都不理想。他们俩都工作的好事。:)
Jesse Chisholm 2014年

我想你可以替换Type.GetType(string.Format(...))使用type.MakeByRefType()
Drew Noakes 2015年

3
该方法仅需要针对每种类型反映一次,而不是针对每次调用反映一次。如果使它成为具有静态成员变量的通用类,则可以重用第一个反射的输出。
安德鲁·希尔

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

6

这样的事情怎么样?

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

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

 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无关紧要?我想不是,但是我仍然认为以这种方式使用异常对我来说是错误的……
Piers Myers 2010年

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

谢谢史蒂文,再好不过了。
鲍勃

不使用转换结果:代码是多余的。
BillW

4

您不能在常规类型上执行此操作。

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

我想虽然您打算将其用于像int和这样的基本类型DateTime。您不能更改这些类型以实现新的接口。


1
我想知道通过使用.net 4中的dynamic关键字是否可行?
Pierre-Alain Vigeant

@Pierre:默认情况下,此方法在C#中使用dynamic关键字将无效,因为它不适用于静态类型。您可以创建自己的动态对象来处理此问题,但这不是默认设置。
史蒂文

4

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

3

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

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

这是另一个选择。

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

我喜欢这个,但是如果没有泛型,您将如何做呢。用例当然是反思。
Sinaesthetic

我添加了一个重载方法,该方法可以进行一些反射性黑客攻击。如果有一种更优雅的解决方法,我都会大声笑gist.github.com/dasjestyr/90d8ef4dea179a6e08ddd85e0dacbc94
Sinaesthetic

2

当我想做几乎所有的事情时,我必须经过艰苦的努力才能实现,并且需要反思。给定T,思考typeof(T)并寻找一个TryParseParse方法,如果找到它,则调用它。


这就是我要建议的。
史蒂文·埃弗斯

2

这是我的尝试。我把它当作“运动”来做。我试图使其与现有的“ 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()类必须提供类型转换,这通常意味着您不支持转换为自定义类型。
伊恩·高德比

1

如您所说,TryParse它不是接口的一部分。它也不是任何给定基类的成员,因为它实际上是static并且static函数不能是virtual。因此,编译器无法确保T实际上有一个名为的成员TryParse,因此这是行不通的。

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


1
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);
    }
}


0

http://blogs.msdn.com/b/davidebb/archive/2009/10/23/using-c-dynamic-to-call-static-members.aspx借来的

当遵循此参考时:如何在C#4.0中使用动态类型调用静态方法?

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

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改编自David Ebbo的文章(它引发了AmbiguousMatchException)


0
public static T Get<T>(string val)
{ 
    return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val);
}

0

以相关方式使用TypeDescriptorTryParse

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

尽管这段代码可以解决问题,但包括解释如何以及为什么解决该问题的说明,确实可以帮助提高您的帖子质量,并可能导致更多的投票。请记住,您将来会为读者回答问题,而不仅仅是现在问的人。请编辑您的答案以添加说明,并指出适用的限制和假设。
嘟嘟声

0

使用上面的信息,这就是我开发的。它将可能直接转换该对象,否则它将将该对象转换为字符串,并为所需的对象类型调用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;
    }

我必须添加另一个函数来支持枚举。似乎Enum解析需要“ where T:struct”属性,并且我希望此方法可用于任何可转换的对象。(可能应在类型中添加一个Convertible属性)。但是,以下一些建议看起来更简单(因此更好)。
D Duffy

0

我在这里提出了很多想法,最后提出了一个非常简短的解决方案。

这是字符串的扩展方法

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

    }

'''


float testValue = 0; 如果(“ 1234” .TryParse <float>(out testValue)){doSomethingGood(); } else {handleTheBadness(); }
JD Hicks

0

T.TryParse ...为什么?

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

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

-2

用于从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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.