如何在通用版本的TryParse()中转换为特定类型?

时间:2009-07-09 23:33:32

标签: c# .net generics casting

我有以下场景,我想传入字符串和泛型类型:

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

在此过程中的某些时刻,我需要将字符串值转换为其T值。但是我不想进行直接转换,因为如果字符串无法转换为类型T,我需要执行一些逻辑。

我在想我可以尝试使用Convert.ChangeType(),但这有一个问题,即如果它不转换它将抛出异常,我将经常运行DoSomeWork()方法,以至于没有依靠try / catch来确定转换是否有效。

所以这让我思考,我知道我将使用数字类型,因此T将是以下任何一种:intuintshort,{{1 },ushortlongulongbytesbytedecimalfloat。知道这一点我认为有可能提出一个更快的解决方案,我知道我将使用数字类型(注意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))是一个问题),或者是否有一个我没有考虑的更好的解决方案。

5 个答案:

答案 0 :(得分:39)

  

t =结果? (T)typedValue:默认(T);

尝试:

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

是的,仿制药有时会有点烦人。

FWIW,我在Convert.ChangeType()周围使用a much simpler wrapper,只是对空字符串进行预检查。除非您将此用于未经检查的用户输入,否则这可能就足够了。

答案 1 :(得分:12)

鉴于此:

  

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

答案 2 :(得分:5)

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

答案 3 :(得分:2)

你可以尝试一些简单的事情

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

答案 4 :(得分:1)

为什么不使用反射并使用内置的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;
}