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

时间:2022-12-25 22:57:14

I have the following scenario where I want to pass in string and a generic type:

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

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

At some point along the way I need to convert the string value to its T value. But I don't want to do a straight convert as I need to perform some logic if the string cannot be converted to type T.

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

I was thinking that I could try using Convert.ChangeType() but this has the problem that if it doesn't convert it will throw an exception and I will be running the DoSomeWork() method often enough to not have to rely on a try/catch to determine whether the convert is valid.

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

So this got me thinking, I know that I will be working with numeric types, hence T will be any of the following: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double. Knowing this I thought that it might be possible to come up with a faster solution working with the fact that I know I will be using numeric types (note if T isn't a numeric type I throw an exception)...

所以这让我思考,我知道我将使用数字类型,因此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;
    }
}

But the above has the problem that I can't write t = result ? (T)typedValue : default(T); as the casting of typedValue to T causes issues and the only way I have been able to get around it thus far is by writing (T)Convert.ChangeType(typedValue, typeof(T)). But if I do this I am just doing another convert.

但上面有一个问题,我不能写t =结果? (T)typedValue:默认(T);因为将typedValue转换为T会导致问题,到目前为止我能够绕过它的唯一方法是编写(T)Convert.ChangeType(typedValue,typeof(T))。但如果我这样做,我只是做另一个转换。

Hence I was wondering if anyone knows how I could fix this problem (if you think doing the ChangeType() is a problem) or if there is a better solution altogether that I haven't considered.

因此,我想知道是否有人知道我如何解决这个问题(如果你认为做ChangeType()是一个问题)或者如果有一个更好的解决方案我没有考虑过。

5 个解决方案

#1


38  

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

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

Try:

尝试:

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

Yes, generics can be kinda annoying at times.

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

FWIW, I use a much simpler wrapper around Convert.ChangeType() that just does a pre-check for empty strings. Unless you're using this for un-checked user input, that'll probably be enough.

FWIW,我在Convert.ChangeType()周围使用了一个更简单的包装器,它只是对空字符串进行预检查。除非您将此用于未经检查的用户输入,否则这可能就足够了。

#2


12  

Given this:

鉴于这种:

hence T will be any of the following: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double.

因此T将是以下任何一种:int,uint,short,ushort,long,ulong,byte,sbyte,decimal,float,double。

I would recommend just using Convert.ChangeType, and not worrying about it. The only time you'll get an exception is when your string is misformatted, in which case, you can return default(T).

我建议只使用Convert.ChangeType,而不用担心它。您获得异常的唯一时间是您的字符串格式错误,在这种情况下,您可以返回默认值(T)。

ie:

即:

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

#3


5  

ToType being the generic parameter here. This works for nullable types, just in case you needed it. You can extract your main method to be a generic converter, that will convert to any type, including nullables.

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


2  

You can try something simple

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

    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


1  

Why not just use reflection and use the built in TryParse methods? Pretty much one for every native type with the exception of Guid.

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

#1


38  

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

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

Try:

尝试:

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

Yes, generics can be kinda annoying at times.

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

FWIW, I use a much simpler wrapper around Convert.ChangeType() that just does a pre-check for empty strings. Unless you're using this for un-checked user input, that'll probably be enough.

FWIW,我在Convert.ChangeType()周围使用了一个更简单的包装器,它只是对空字符串进行预检查。除非您将此用于未经检查的用户输入,否则这可能就足够了。

#2


12  

Given this:

鉴于这种:

hence T will be any of the following: int, uint, short, ushort, long, ulong, byte, sbyte, decimal, float, double.

因此T将是以下任何一种:int,uint,short,ushort,long,ulong,byte,sbyte,decimal,float,double。

I would recommend just using Convert.ChangeType, and not worrying about it. The only time you'll get an exception is when your string is misformatted, in which case, you can return default(T).

我建议只使用Convert.ChangeType,而不用担心它。您获得异常的唯一时间是您的字符串格式错误,在这种情况下,您可以返回默认值(T)。

ie:

即:

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

#3


5  

ToType being the generic parameter here. This works for nullable types, just in case you needed it. You can extract your main method to be a generic converter, that will convert to any type, including nullables.

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


2  

You can try something simple

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

    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


1  

Why not just use reflection and use the built in TryParse methods? Pretty much one for every native type with the exception of Guid.

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