我正在按照MSDN的推荐IComparer<T>从Comparer<T>类中派生出一个实现.例如:
public class MyComparer : Comparer<MyClass>
{
private readonly Helper _helper;
public MyComparer(Helper helper)
{
if (helper == null)
throw new ArgumentNullException(nameof(helper));
_helper = helper;
}
public override int Compare(MyClass x, MyClass y)
{
// perform comparison using _helper
}
}
Run Code Online (Sandbox Code Playgroud)
然而,根据这一办法,在MyComparer类继承的Default,并Create从静态成员Comparer<T>类.这是不可取的,因为所述成员的实现与我的派生类无关,并且可能导致误导行为:
// calls MyClass.CompareTo or throws InvalidOperationException
MyComparer.Default.Compare(new MyClass(), new MyClass());
Run Code Online (Sandbox Code Playgroud)
由于所需的Helper参数,我的比较器不能有一个默认实例,也不能从a初始化它自己Comparison<T>,所以我不能用有意义的实现隐藏继承的静态成员.
这种情况的推荐做法是什么?我正在考虑三种选择:
IComparer<T>手动实现,而不是从中派生Comparer<T>,以避免继承所述静态成员
保留继承的静态成员,并假设消费者知道不使用它们
使用抛出的新实现隐藏继承的静态成员InvalidOperationException:
public static new Comparer<MyClass> Default
{
get { throw new InvalidOperationException(); }
}
public static new Comparer<MyClass> Create(Comparison<MyClass> comparison)
{
throw new InvalidOperationException();
}
Run Code Online (Sandbox Code Playgroud)不要继承Comparer<T>.这是代码共享的经典滥用继承.应该使用继承来实现Liskov替换原则(LSP).继承代码重用是一种破解,因为正如您所发现的那样,它会在您的公共API表面中暴露"垃圾".
这不是LSP违规,因为没有基本类型合同被破坏.然而,这是对继承的滥用.问题是内部构件以API用户可能错误依赖的方式暴露.它还会阻碍将来的实现更改,因为删除基类可能会破坏用户.
您是否能够容忍这种肮脏程度取决于您对公共API表面的质量标准.如果您不关心这一点,那么继续坚持DRY而不遵守LSP.如果十亿行代码依赖于您的类,您当然不希望暴露脏基类.这里的问题成为封装(消费者不需要了解比较器的实现)和创建类时保存工作之间的权衡.
你提出了DRY原则.我不确定这是违反DRY的情况.干尝试以防止重复的代码变得不一致,并尝试防止重复的维护工作.由于这里的重复代码永远不会改变(空订购是契约的)我不认为这是一个有意义的DRY违规.相反,它只是在创建实现时保存工作.
实现IComparer<T>很容易,所以这样做.我认为不再需要实施IComparer了.默认实现并不多.如果您关心空输入,则必须在您自己的比较方法中复制该逻辑.您实现的代码重用几乎没有.
我正在考虑实现自己的ComparerBase
这将是同一问题的一个例子.也许您可以改为创建一个实现样板null和类型处理的静态帮助器方法.该静态帮助程序不会向API用户公开.这是"继承的构成".
隐藏静态成员真的很令人困惑.根据呼叫站点的细微变化,将调用不同的方法.而且,它们都不是有用的.
我不太关心静态方法现在可以通过不同的类型名称获得的事实.这些方法并没有真正继承.它们仅作为C#功能提供.我相信这是针对版本弹性的.从不推荐它,各种工具会围绕此产生警告.我不会太在意这件事.例如,每个Stream"继承"某些静态成员,例如Stream.Null和/ Stream.Synchronized或它们被称为什么.没有人认为这是一个问题.
| 归档时间: |
|
| 查看次数: |
145 次 |
| 最近记录: |