我试图创建一个通用扩展,使用'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方法的所有类型。它也适用于这些(结构体)的可空版本,但枚举除外。

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

其他回答

这是另一个选择。

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

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

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

你应该使用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相关的方式使用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;
    }
}