为什么不总是使用泛型?

时间:2010-10-28 07:37:40

标签: .net generics

我正在阅读有关仿制药的现有帖子。如果Generics有很多优点,如Type安全,没有装箱/拆箱的开销,而且速度很快,为什么不总是使用呢?你为什么要使用非通用对象?

已编辑(问题进一步扩展如下)

我有点困惑。几个月前我读到Generics的最后一次,我读到如果参数中的Type是可变的,那么应该使用Generic来防止错误。但我现在正在阅读的是,泛型将实现限制为固定类型,而非泛型对象允许您在运行时定义参数类型。

请帮我看看我错过了什么?

其次,当您在团队中工作并且您的代码可共享时,在适当的OOP设计(泛型等)中使用这些类型的构造会很有帮助。对于一个规模较小的独立程序员,谁知道Type必须参与哪个类型,似乎没有必要担心,使用Generic或Non-Generic类型之间差别不大。这准确吗?

5 个答案:

答案 0 :(得分:11)

一般来说,应该 - 但是,有时(特别是在编写库代码时)无法(或肯定不方便)了解调用类型(即使是{ {1}}),因此非泛型类型(主要是TIList等接口)非常有用。数据绑定就是一个很好的例子。事实上,涉及反思的任何通常使用泛型变得更加困难,并且因为(通过反射的本质)你已经丢失这些好处,你也可以只需删除非通用代码。反思和泛型不是的好朋友

答案 1 :(得分:4)

泛型可能是快速且类型安全的,但也增加了复杂性(另一个维度可以变化,并且必须由程序员理解)。谁将维护您的代码?并非所有泛型(或lambdas,或依赖注入,或......)的技巧都值得。想想你将要解决的问题,以及该问题的哪些部分将来可能会发生变化。为那些案件设计。最灵活的软件太复杂,无法由凡人程序员维护。

答案 2 :(得分:2)

有时“对象”集合是不可避免的。当同一控件/集合中有多种类型时,通常会发生这种情况 - 它们共有的唯一类型是“对象”,因此这是您收藏的最佳类型。

PropertyGrid可以看到另一个不时弹出的对象(非集合相关)。第三方属性网格可以允许您附加“验证器”,该验证器返回用户是否可以接受网格上给定属性的用户新值。由于PropertyGrid不知道它将显示哪些属性,它可以给验证器提供的最好的是一个对象 - 即使验证器确切地知道它将被调用的类型。

但是根据Mark的回答 - .NET中的大多数(全部?)非泛型集合仅仅是出于遗留原因。如果今天重新编译.NET,您可以确定标准库看起来会非常不同。

答案 3 :(得分:0)

泛型是一种普遍的方式。增加的复杂性可能是反对它的一个论点,但是在大多数情况下完全值得考虑的好处。作为一般的经验法则,拥抱它。反思是另一种情况,仿制药使其变得更难。但是,只有在没有意义的情况下,我才会避免使用泛型。

我希望避免使用泛型的一种情况是使方法通用,因为它不提供泛型的预期目的并且可能会产生误导。 引入了泛型以提高类型安全性和性能(当涉及到价值类型时)。如果它既不服务,那么通用方法可能会误导开发人员。例如,请考虑this question

中的代码段
public void DoSomething<T>(T request)
{
    if (request == null) 
        throw new ArgumentNullException("request");

    if (request is ISomeInterface)
        DoSomething();
    else
        DoSomethingElse();
}

上述方法一般没有处理任何问题。当你在泛型方法中进行大量的类型检查时,情况会更糟:

public void DoSomething<T>(T request)
{
    if (typeof(T) == typeof(X))
        DoSomething();
    else if (typeof(T) == typeof(Y))
        DoSomethingElse();
    ...
}

完全打败了泛型的逻辑。有更好的模式可以避免类型检查,但如果绝对必要,那么接受object作为参数是我更喜欢的。另一个类似的情况是,在通用方法中,值类型无论如何都是盒装的:

public void DoSomething<T>(T request)
{
    var type = request.GetType(); //boxed already in case of value types.

    //or other reflection calls which involves boxing
}

通过查看方法定义,人们很想念这种方法没有拳击惩罚,但让呼叫者清楚地知道它是一种好方法。我喜欢它:

public void DoSomething(object request) //boxes here
{
    var type = request.GetType(); 

    //or other reflection calls which involves boxing
}

除此之外,我一直都喜欢仿制药。


两者行为不同的边缘情况是dynamic

public void Generic<T>(T request)
{
}

public void Object(object request)
{
}

dynamic d = null;
Object(d); //no issues
Generic(d); //run-time explosion; can not infer type argument

不是一个需要考虑的因素,只是一个值得考虑的行为的愚蠢差异。

答案 4 :(得分:0)

泛型使代码更具可重用性,增加了类型安全性等。如果您看到任何好的API(Jdk,Scala等),那么随处可见泛型。 泛型的最大挑战是,要习惯它有点棘手,因此许多程序员发现泛型不是可读的代码。

但是接下来我们应该解决实际问题(桥接知识差距),而不要避免泛型。