Подтвердить что ты не робот

Как преобразовать в определенный тип в общей версии TryParse()?

У меня есть следующий сценарий, где я хочу передать строку и общий тип:

public class Worker {
    public void DoSomeWork<T>(string value) 
        where T : struct, IComparable<T>, IEquatable<T> { ... }
}

В какой-то момент по пути мне нужно преобразовать значение строки в значение T. Но я не хочу делать прямой конвертировать, поскольку мне нужно выполнить некоторую логику, если строка не может быть преобразована в тип T.

Я думал, что могу попробовать использовать Convert.ChangeType(), но у этой проблемы есть проблема, что если она не будет конвертироваться, это вызовет исключение, и я буду использовать метод DoSomeWork() достаточно часто, чтобы не полагаться на try/catch, чтобы определить, действительно ли преобразован.

Итак, это заставило меня задуматься, я знаю, что буду работать с числовыми типами, следовательно, T будет любым из следующих: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double. Зная это, я подумал, что можно было бы найти более быстрое решение, работающее с тем, что я знаю, что буду использовать числовые типы (обратите внимание, если T не является числовым типом, я выдаю исключение)...

public class NumericWorker {
    public void DoSomeWork<T>(string value) 
        where T : struct, IComparable<T>, IEquatable<T> 
    { 
        ParseDelegate<T> tryConverter = 
           SafeConvert.RetreiveNumericTryParseDelegate<T>();
        ... 
    }
}


public class SafeConvert
{
    public delegate bool ParseDelegate<T>(string value, out T result);

    public static ParseDelegate<T> RetreiveNumericTryParseDelegate<T>()
        where T : struct, IComparable<T>, IEquatable<T>
    {
        ParseDelegate<T> tryParseDelegate = null;

        if (typeof(T) == typeof(int))
        {
           tryParseDelegate = (string v, out T t) =>
              {
                 int typedValue; 
                 bool result = int.TryParse(v, out typedValue);
                 t = result ? (T)typedValue : default(T); 
                 //(T)Convert.ChangeType(typedValue, typeof(T)) : default(T);
                 return result;
              }; 
        }
        else if (typeof(T) == typeof(uint)) { ... }
        else if (typeof(T) == typeof(short)) { ... }
        else if (typeof(T) == typeof(ushort)) { ... }
        else if (typeof(T) == typeof(long)) { ... }
        else if (typeof(T) == typeof(ulong)) { ... }
        else if (typeof(T) == typeof(byte)) { ... }
        else if (typeof(T) == typeof(sbyte)) { ... }
        else if (typeof(T) == typeof(decimal)) { ... }
        else if (typeof(T) == typeof(float)) { ... }
        else if (typeof(T) == typeof(double)) { ... }

        return tryParseDelegate;
    }
}

Но вышеизложенная проблема состоит в том, что я не могу написать t = result ? (T)typedValue : default(T);, поскольку приведение typedValue в T вызывает проблемы и единственный способ, которым я смог обойти это до сих пор, - написать (T)Convert.ChangeType(typedValue, typeof(T)). Но если я это сделаю, я просто делаю другой конверт.

Следовательно, мне было интересно, знает ли кто-нибудь, как я мог бы исправить эту проблему (если вы считаете, что проблема с ChangeType() является проблемой) или если есть лучшее решение, которое я не рассматривал.

4b9b3361

Ответ 1

t = результат? (T) typedValue: по умолчанию (T);

Попробуйте:

t = result ? (T)(object)typedValue : default(T);

Да, дженерики могут временами раздражать.

FWIW, я использую гораздо более простой обертку вокруг Convert.ChangeType(), который просто выполняет предварительную проверку для пустых строк. Если вы не используете это для неконтролируемого ввода пользователем, этого, вероятно, будет достаточно.

Ответ 2

Учитывая это:

следовательно, T будет любым из следующего: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double.

Я бы порекомендовал просто использовать Convert.ChangeType и не беспокоиться об этом. Единственный раз, когда вы получите исключение, - это когда ваша строка неверно определена, и в этом случае вы можете вернуть значение по умолчанию (T).

т

try
{
    result = Convert.ChangeType(value, typeof(T));
}
catch
{
    result = default(T);
}

Ответ 3

ToType является общим параметром здесь. Это работает для типов с нулевым значением, на случай, если вам это нужно. Вы можете извлечь свой основной метод как универсальный конвертер, который будет конвертировать в любой тип, включая nullables.

    ToType result = default(ToType);    

    result = ChangeType<ToType>(typedValue);


  private T ChangeType<T>(object o)
{
   Type conversionType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
   return (T)Convert.ChangeType(o, conversionType);
}

Ответ 4

Вы можете попробовать что-то простое

    public static T ConvertValue<T,U>(U value) where U : IConvertible {
        return (T)ConvertValue(value, typeof(T));
    }

    public static object ConvertValue(IConvertible value, Type targetType) {
        return Convert.ChangeType(value, targetType);
    }

Ответ 5

Почему бы просто не использовать отражение и использовать встроенные методы TryParse? Довольно много для каждого родного типа, за исключением Guid.

public static Parser<T> GetParser<T>(T defaultResult)
    where T : struct
{
    // create parsing method
    Parser<T> parser = (string value, out T result) =>
    {
        // look for TryParse(string value,out T result)
        var parseMethod = 
            typeof(T).GetMethods()
                     .Where(p => p.Name == "TryParse")
                     .Where(p => p.GetParameters().Length == 2)
                     .Single();

        // make parameters, leaving second element uninitialized means out/ref parameter
        object[] parameters = new object[2];
        parameters[0] = value;

        // run parse method
        bool success = (bool)parseMethod.Invoke(null, parameters);

        // if successful, set result to output
        if (!success)
        {
            result = (T)parameters[1];
        }
        else
        {
            result = defaultResult;
        }

        return success;
    };

    return parser;
}