定义采用IEnumerable类型约束或基类的方法?

时间:2018-01-29 14:10:14

标签: c# generics

我之前就using generic vs base class in a method taking in a List<T>问了一个问题。很快就指出,您无法将List<Derived>传递给期望List<Base>的方法。

进一步探索(以及this one等有用的链接)表明,虽然您无法将List<Derived>传递给List<Base>,但可以传递List<Derived>IEnumerable<Base>(由于.NET 4中的逆变/协方差)。

就像我之前的问题一样,我在这里提出了同样的问题:因为使用这两个选项似乎没有什么区别,哪个会更好?它是严格的基于风格的东西,还是使用一个而不是另一个?

选项A:void DoWork<TBase>(IEnumerable<TBase> objs) where TBase : Base;

选项B:void DoWork(IEnumerable<Base> objs);

2 个答案:

答案 0 :(得分:1)

选项A

import calendar

{(k, m): calendar.monthrange(k, m)[1] for k in range(2014, 2018) for m in range(1, 13)}

# {(2014, 1): 31,
#  (2014, 2): 28,
#  (2014, 3): 31,
#  etc

根据void DoWork<TBase>(IEnumerable<TBase> objs) where TBase : Base; 约束IEnumerable中的元素。如果TBase继承自TBase,则Base不能包含IEnumerable

选项B

Base

void DoWork(IEnumerable<Base> objs); 可以包含IEnumerable或从中继承的任何内容。

在任何一种情况下,除非有一些丑陋的类型检查,否则每个方法只能根据Base的定义与IEnumerable中的元素进行交互,因为这些都是它知道&#34;关于他们。

如果方法返回BaseBase,则差异会更有意义。然后调用者可以传入TBase并获得一个投射为TBase的结果。如果类型对调用者的影响大于对此方法的影响,那么它就不希望传入一个继承自TBase的参数并将其结果转换为TBase

答案 1 :(得分:1)

选项A稍微多一点,因为DoWork(new TDerived[0])是与DoWork(new TBase[0])不同的方法调用。在抖动的输出中会有很多共享代码(因为TBaseTDerived必须是实际上不同的引用类型,并且有一个来自另一个),但是这有点多了。

理论上如果DoWork调用虚拟方法并且TDerived被密封,那么抖动可能会利用那些可以带来轻微好处的方法,但我可以我很确定由于代码共享而不会发生。如果它确实发生了,那么差异就会很小。

因此,在所有选项中B赢得了不更多版本的泛型方法,而选项A没有补偿优势。

但是,如果是选项A可能更有用:

TBase DoWorkAndReturnSomething(IEnumerable<TBase> objs) where TBase : Base

避免必须投射结果的地方。如果您实际投射结果,这只会是一个优势。如果您继续将其用作Base或将结果传递给采用Base的方法,那么您无法获得任何补偿,以补偿您额外的抖动和额外的概念复杂性。

因此每种方法都有利弊。对于更广泛的案例,非泛型可能更好,但有时候带约束的泛型形式确实有优势。