我之前就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);
答案 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;关于他们。
如果方法返回Base
或Base
,则差异会更有意义。然后调用者可以传入TBase
并获得一个投射为TBase
的结果。如果类型对调用者的影响大于对此方法的影响,那么它就不希望传入一个继承自TBase
的参数并将其结果转换为TBase
。
答案 1 :(得分:1)
选项A稍微多一点,因为DoWork(new TDerived[0])
是与DoWork(new TBase[0])
不同的方法调用。在抖动的输出中会有很多共享代码(因为TBase
和TDerived
必须是实际上不同的引用类型,并且有一个来自另一个),但是这有点多了。
理论上如果DoWork
调用虚拟方法并且TDerived
被密封,那么抖动可能会利用那些可以带来轻微好处的方法,但我可以我很确定由于代码共享而不会发生。如果它确实发生了,那么差异就会很小。
因此,在所有选项中B赢得了不更多版本的泛型方法,而选项A没有补偿优势。
但是,如果是选项A可能更有用:
TBase DoWorkAndReturnSomething(IEnumerable<TBase> objs) where TBase : Base
避免必须投射结果的地方。如果您实际投射结果,这只会是一个优势。如果您继续将其用作Base
或将结果传递给采用Base
的方法,那么您无法获得任何补偿,以补偿您额外的抖动和额外的概念复杂性。
因此每种方法都有利弊。对于更广泛的案例,非泛型可能更好,但有时候带约束的泛型形式确实有优势。