3

最近我正在实现一个Trie数据结构,并决定节点可以存储不同类型的数据或使其实现有所不同,所以我选择了Node<T>. 然后当我进入构造 Trie 的算法时,我意识到它需要更深入的 Node 知识,所以我限制泛型类使用INode接口。这允许更大的灵活性,但在泛型类的上下文中感觉不对。

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

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

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

澄清这些观点有什么帮助吗?

4

3 回答 3

5

当面临选择使用具有接口约束的泛型与具有接口类型的非泛型时,我只会在作为泛型参数传递的部分或全部类型是值类型的情况下使用泛型+接口。这将防止我的实现在处理我struct的 s 时需要昂贵的装箱和拆箱。

例如,如果接口恰好是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);
        ...
    }
}

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

于 2013-10-03T08:39:28.360 回答
4

我认为对泛型参数施加约束没有错。有一个通用的参数并不意味着“这对任何事情都有效”,它意味着有不止一种方法可以使代码有意义。

它实际上可能会公开一个完全通用的概念,例如List<T>,但它可能会公开一个仅在某些上下文中有意义的概念(例如Nullable<T>仅对不可为空的实体有意义)

约束只是您用来告诉世界在什么情况下该类有意义的机制,并使您能够以合理的方式实际使用该(受约束的)参数,即调用Dispose实现的事物IDisposable

极端情况是当上下文非常受限时,即如果只有两种可能的实现怎么办?实际上,我当前的代码库中有这种情况,并且我使用泛型。我需要对某些数据点进行一些处理,目前(以及在可预见的将来)只有两种数据点。原则上,这是我使用的代码:

interface IDataPoint 
{ 
   SomeResultType Process();
}

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

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

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

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

这样,我可以在处理器中使用 aList<T>和 an而不是 a and在我的场景中这将是不正确的,因为我需要一个处理器来处理更具体的数据类型,即实现.Action<T>List<IDataPoint>Action<IDataPoint>IDataPoint

如果我需要一个可以处理任何东西的处理器,只要它是一个,删除它的通用性并简单地在代码中IDataPoint使用可能是有意义的。IDataPoint

此外,@dasblinkenlight 的回答中提出的观点非常有效。如果泛型参数既可以是结构也可以是类,那么使用泛型将避免任何装箱。

于 2013-10-03T08:29:10.370 回答
2

object泛型通常用于使用接口或基类(包括传入的可能是对特定类型进行操作的表达式。

因此,如果您从另一端接近逻辑。关于类型限制的决定应该与选择函数参数类型时的决定相同。

于 2013-10-03T08:44:53.590 回答