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

当前回答

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

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

其他回答

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

正如你所说,TryParse不是接口的一部分。它也不是任何给定基类的成员,因为它实际上是静态的,而静态函数不能是虚函数。编译器没有办法保证T确实有一个叫TryParse的成员,所以这行不通。

正如@Mark所说,您可以创建自己的接口并使用自定义类型,但对于内置类型就不太走运了。

我使用Charlie Brown的答案作为Json.NET的JObject扩展方法。

public static class JObjectExtension
{
    public delegate bool TryParseHandler<T>(string value, out T result);

    public static bool TryParsePropertyAs<T>(
        this JObject jObject,
        string propertyName,
        TryParseHandler<T> parser,
        out T value)
    {
        value = default;

        if (!jObject.TryGetValue(propertyName, out var jToken)) return false;

        if (!parser(jToken.Value<string>(), out T result)) return false;

        value = result;

        return true;
    }
}

T.TryParse……为什么?

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

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

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