将值类型转换为泛型

3

我有一个通用类,需要将其限制为只能使用值类型(例如 int、float 等)。我有一个方法,根据类型的测试调用 Parse 方法。例如:

class MyClass<T>
{
    ...

    private static T ParseEntry(string entry)
    {
        if(typeof(T) == typeof(float))
        {
            return (T) float.Parse(entry);
        }

        if(typeof(T) == typeof(int))
        {
            .... you get the idea
        }
    }
}

将T限制为struct无效,如果可能的话,我真的想避免装箱/拆箱。有什么建议吗?
编辑:为了更深入地了解这一点。我注意到我正在开发的库中有两个类具有非常相似的属性/方法等,唯一的区别是数据的基础类型(int或float)。这导致我使用泛型进行设计。唯一的问题是由于调用特定的Parse方法取决于它是浮点数还是整数。我可以通过装箱/拆箱来解决,但如果可能的话,我真的想避免这种情况。
5个回答

12
private static T ParseEntry(string entry)
{
    TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
    return (T) conv.ConvertFromString(entry);
}

这种方法的优点是可以与任何类型转换器一起使用(或者您可以在运行时添加自己的类型转换器)。它确实有装箱,但是实际上,也不是那么糟糕。如果这是您最大的问题,那么您正在错误地处理此事。


我运行了上面的代码,与仅调用Parse方法相比,我发现它要慢5倍。不确定是否同意Skeet的博客中提到装箱是“噪音”这一说法。我同意你不应该为了避免它而跳过设计障碍,但如果可能的话,应该尽量避免使用。 - Brian Triplett

6

很遗憾,您无法创建描述类型的约束。例如,您无法编写以下内容:

class MyClass<T> where T : int { ... } // won't compile

您最好将约束留给struct,但在实例化类时添加一些运行时检查,以确保T只是受支持的类型之一。由于您没有详细说明如何使用您的类-很难提供替代设计建议,以实现更好的类型安全性。
编辑:您应该查看Marc和其他人推荐的类型转换器选项。这似乎是正确的方法。 编辑:我想到的一个可能的想法是,使Parse()方法实际上成为一个委托:Func<string,T> - 您在构造期间分配。这将使您能够:
  1. 避免低效和笨拙的if / else逻辑。
  2. 改进将来对您的类使用其他值类型(结构体、BigDecimal等)
这是我所说的代码示例:
class MyClass<T>
{
    private readonly Func<string,T> ParseEntry;

    public MyClass( Func<string,T> parser )    
    {
        ParseEntry = parser;
    }
}

public static class AvailableParsers
{
    public static Func<string,int> ParseInt = s => int.Parse( s );
    public static Func<string,float> ParseFloat = s => float.Parse(s);
}

你可以在这里了解可用的约束条件。唯一可用的约束条件包括:
  • struct(可选)
  • new()(可选)
  • 接口约束(可选,允许多个)
  • 基类约束(可选,仅允许一个)
  • 裸约束(例如 where T : TResult)

现在我在想... CLR是否允许将System.Int32作为约束条件?我们能否使用Jon Skeet在Unconstrained Melody上使用的相同技巧来实现这一点? - R. Martinho Fernandes
@Martinho:不行,而且这样做有什么意义呢?类似MyFunc<T>(T num) where T : int的方法签名与MyFunc(int num)实际上是相同的。 - LukeH
委托解决方案似乎是最简单的。构造函数是库内部的(即用户可以访问已创建的对象但无法创建它们),因此传递额外参数没有太大问题。 - Brian Triplett

6

也许有些愚蠢,但是——如果你只考虑了有限种类型,为什么不直接重载方法呢?就像*Writer.Write() 方法一样,为每个“基础”类型提供了重载方法。

简而言之:如果你无论如何都会基于类型执行不同的操作(这有点违背了传递任意类型而执行相同操作的目的),那为什么还要使用泛型?


编辑:考虑到使用场景,我认为你实际上需要使用Convert.ChangeType

像这样:

class MyClass<T> where T: IConvertible
{
    private static T ParseEntry(string entry)
    {
        return (T)Convert.ChangeType(entry, typeof(T));
    }
}

1

我想我没有看到这里的价值... 为什么不使用Convert类呢?


0

就像Bushkin先生所说,你不能这样做。因此,你的另一个选择是为每种原始类型简单地创建多个方法重载。

此外,想象一下,如果你不必处理原始类型,但你真的想要处理所有结构体——这仍然是不可能的,因为结构体不能保证实现一个名为IParsable的(迄今为止虚构的)接口以转换为T。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接