具有接口约束的通用类与实现接口的类

时间:2013-10-03 08:22:40

标签: c# generics interface

最近我实现了一个Trie数据结构,并确定节点可以存储不同类型的数据,或者它的实现方式各不相同,所以我去了Node<T>。然后当我进入构建Trie的算法时,我意识到它需要更多关于Node的知识,所以我限制泛型类使用INode接口。这允许更大的灵活性,但在泛型类的上下文中感觉不对。

通用类对实现接口的类有不同的用例。例如,List<T> - 算法可以在不依赖于相关抽象集的情况下工作。实现接口的类可能需要多态/ DI,但接口将更加专业化。

在什么情况下其他人应用泛型类T,其中T可以实现更专业的接口?

我认为当T不需要公开操作/数据时会使用泛型类,虽然我可以看到在T实现IDisposable或其他更通用的接口时可以使用泛型类。

有什么帮助澄清这些要点?

3 个答案:

答案 0 :(得分:5)

当面临选择使用具有接口约束的泛型而非使用接口类型的非泛型时,我将仅在通用作为泛型参数传递的某些或所有类型为值类型的情况下使用泛型+接口。这样可以防止我的实施在处理我的struct时需要昂贵的装箱和拆箱。

例如,如果接口碰巧是IComparable,我肯定更喜欢带有约束的泛型,因为它可以让我在处理原语时避免装箱。

请注意,为泛型类提供功能的另一种方法是将委托与值一起传递。例如,如果你打算做这样的事情

interface IScoreable {
    decimal GetScore(object context);
}
class Node<T> where T : IScoreable {
    ...
    void DoSomething(T data) {
        var score = data.GetScore(someContext);
        ...
    }
}

你也可以这样做:

class Node<T> {
    private Func<T,object,decimal> scorer;
    public Node(Func<T,object,decimal> scorer) {
        this.scorer = scorer;
    }
    ...
    void DoSomething(T data) {
        var score = scorer(data, someContext);
        ...
    }
}

第二种解决方案允许您将评分功能与正在评分的类型“解耦”,但代价是让调用者编写更多代码。

答案 1 :(得分:4)

我认为在泛型参数上放置约束没有错。拥有泛型参数并不意味着“这将适用于任何事情”,这意味着代码有多种方式是有意义的。

它实际上可能会暴露一个完全通用的概念,例如List<T>,但它可能会暴露一个仅在某些情境中有意义的概念(例如Nullable<T>仅对非可空实体有意义)

约束只是用来告诉世界这个类在什么情况下有意义的机制,并且能够让你以合理的方式实际使用那个(约束的)参数,即调用Dispose on实施IDisposable

的事情

这种情况的极端情况是当上下文受到很大约束时,即如果只有两种可能的实现呢?我实际上在我当前的代码库中有这种情况,我使用泛型。我需要对某些数据点进行一些处理,目前(在可预见的将来),只有两种数据点。原则上,这是我使用的代码:

interface IDataPoint 
{ 
   SomeResultType Process();
}

class FirstKindDataPoint : IDataPoint 
{
   SomeResultType Process(){...}
};

class SecondKindDataPoint : IDataPoint 
{
   SomeResultType Process(){...}
};

class DataPointProcessor<T> where T: IDataPoint
{
   void AcquireAndProcessDataPoints(){...}
}

即使在这种受限制的环境中也是有道理的,因为我只有一个处理器,因此只需要处理一个逻辑,而不是两个独立的处理器,我将不得不尝试保持同步。

这样我可以在处理器中使用List<T>Action<T>而不是List<IDataPoint>Action<IDataPoint>,这在我的方案中是不正确的,因为我需要一个处理器用于更具体的数据类型,即实现IDataPoint

如果我需要一个可以处理任何内容的处理器,只要它是IDataPoint,删除它的通用性就行了,只需在代码中使用IDataPoint

此外,@ dasblinkenlight的回答中提出的观点非常有效。如果泛型参数可以是结构和类,而不是使用泛型,则可以避免任何限制。

答案 2 :(得分:2)

泛型通常用于使用接口或基类(这包括object)是不够的,例如,您担心函数的返回值是原始类型而不仅仅是接口,或者您传入的参数可能是对特定类型进行操作的表达式。

所以如果你从另一端接近逻辑。类型限制的决定应与选择函数参数类型时的决策相同。