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

Posted

技术标签:

【中文标题】如何在 TryParse() 的泛型版本中转换为特定类型?【英文标题】:How can I convert to a specific type in a generic version of TryParse()? 【发布时间】:2009-07-09 23:33:31 【问题描述】:

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

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

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

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

所以这让我想到,我知道我将使用数字类型,因此 T 将是以下任何一种:intuintshortushortlong、@ 987654331@、bytesbytedecimalfloatdouble。知道这一点后,我认为可能会提出一个更快的解决方案,因为我知道我将使用数字类型(请注意,如果 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);,因为typedValueT 的转换会导致问题,到目前为止我能够解决它的唯一方法是写@987654342 @。但如果我这样做,我只是在做另一个转换。

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

【问题讨论】:

【参考方案1】:

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

试试:

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

是的,泛型有时会有点烦人。

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

【讨论】:

我使用它的目的是验证用户输入... :( 这就是为什么我不想使用 changetype 和 try/catch 因为谁知道用户会输入什么... 嘿,好的。 :-) FWIW,我不会太担心捕获ChangeType() 抛出的异常的效率方面,但是如果您担心样式,那么您的解决方案看起来不错。请注意,您可以通过将 API 简化为单个泛型方法来简化 API,其中 T 由 out 参数隐式指定。 为了简单起见,你说的“其中 T 由 out 参数隐式指定”是什么意思?? 你的意思是在委托内部进行类型测试...如果是这样,我这样做的原因是为了性能,这意味着一旦我进行测试并返回一个委托,我就可以运行尽可能多地委托,而性能副作用最小...... 啊,明白了 - 想知道代表的意义是什么。希望能帮助到你! :-)【参考方案2】:

鉴于此:

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

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

即:

try

    result = Convert.ChangeType(value, typeof(T));

catch

    result = default(T);

【讨论】:

正如我给@Shog9 的评论中提到的那样,问题是因为我使用它来验证用户输入我担心生成异常的开销。 afaik 生成一个异常,您知道每次运行该方法时可能会发生一次异常,这是一项昂贵的操作,因此我尝试改用委托。考虑到您建议的我尝试使用的委托方法的版本仍然值得使用,您会说吗? 就个人而言,我仍然会使用它。在这种情况下,我不会担心异常的开销,特别是因为如果它用于用户输入,您不需要在紧密循环中调用它(因为用户可能无法像您可以处理异常)。即使使用其他方法,您的 switch 语句、委托调用等也会有开销,与我上面的 6 行相比,维护起来很糟糕。 我同意里德的观点。异常在最坏的情况下(冷)可能需要几十毫秒的时间;但如果您正在验证 one 用户输入字符串,那几乎不会引起注意。 结果 = (T)Convert.ChangeType(value, typeof(T));您还必须从 ChangeType 进行转换。【参考方案3】:

ToType 是这里的通用参数。这适用于可为空的类型,以防万一您需要它。您可以将您的主要方法提取为通用转换器,它将转换为任何类型,包括可为空的。

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

【讨论】:

如果我使用这种方法,这如何解决对 try/catch 块的需求?? 不会抛出异常,因为您使用的是 TryParse。如果 TryParse 的结果为真,那么除非您传入完全不同的通用参数,否则转换将起作用。这是你的困境吗? 抱歉,您不能传递不同的泛型参数,因为您有一个关于 typeof(T) 的 if 语句......那么您的问题到底是什么?【参考方案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;

【讨论】:

如果您对其进行测试,您可以在 11 秒内完成超过 100,000 次解析。我使用耗时 14 秒的原生表达式树与 .net 4 进行了比较(尽管在 .net 4 发布时可能不会反映性能)

以上是关于如何在 TryParse() 的泛型版本中转换为特定类型?的主要内容,如果未能解决你的问题,请参考以下文章

java 如何获得List的泛型类型

Java中的泛型

如何判断我的泛型类型是不是支持某个接口并为函数来回转换到该接口? [复制]

Java开发知识之Java中的泛型

Java中的泛型的问题?

Java 泛型概念相关面试题