Универсальный TryParse - PullRequest
181 голосов
/ 03 июня 2010

Я пытаюсь создать общее расширение, которое использует 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;
}

Это работает довольно хорошо, но я думаю, что использование исключений таким образом не подходит мне.

Update2:

Изменено для передачи типа, а не для использования обобщений:

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

Ответы [ 21 ]

2 голосов
/ 21 мая 2014

Это моя попытка. Я сделал это как «упражнение». Я попытался сделать его таким же, как использовать его как существующие " Convert.ToX () " - и т. Д. Но это метод расширения:

    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; }
    }
2 голосов
/ 03 июня 2010

Когда я хотел сделать почти эту точную вещь, мне пришлось реализовать ее трудным способом, учитывая рефлексию. Получив T, подумайте о typeof(T) и найдите метод TryParse или Parse, вызвав его, если вы его нашли.

1 голос
/ 20 марта 2019
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);
    }
}
1 голос
/ 03 июня 2010

Как вы сказали, TryParse не является частью интерфейса. Он также не является членом какого-либо данного базового класса, поскольку на самом деле функции static и static не могут быть virtual. Таким образом, компилятор не может гарантировать, что T на самом деле имеет член с именем TryParse, так что это не работает.

Как сказал @Mark, вы можете создать свой собственный интерфейс и использовать пользовательские типы, но вам не повезло со встроенными типами.

0 голосов
/ 07 июля 2019

Используя информацию выше, это то, что я разработал. Это позволит преобразовать объект напрямую, в противном случае он преобразует объект в строку и вызовет метод 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;
    }
0 голосов
/ 22 мая 2019

С TypeDescriptor использованием класса TryParse связанным способом:

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 голосов
/ 29 октября 2013
public static T Get<T>(string val)
{ 
    return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromInvariantString(val);
}
0 голосов
/ 01 октября 2013

Версия для получения потомков от 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;
    }
}
0 голосов
/ 03 июня 2010

Это вопрос «общих ограничений». Поскольку у вас нет определенного интерфейса, вы застряли, если не последуете советам из предыдущего ответа.

Для документации по этому, проверьте следующую ссылку:

http://msdn.microsoft.com/en-us/library/ms379564(VS.80).aspx

Он показывает вам, как использовать эти ограничения, и должен дать вам еще несколько подсказок.

0 голосов
/ 22 октября 2011

Мне удалось получить что-то, что работает, как это

    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 адаптирован из статьи Дэвида Эббо (он выдавал исключение AmbiguousMatchException)

...