C# 接口/抽象类编码标准
我发现了一个提议中的C#编码标准,其中规定“尝试提供一个与所有抽象类的接口”。有人知道这样做的理由吗 如果不看原始文章,我猜原始作者是在建议它的可测试性,并允许使用MoQ、rhinomock等工具轻松模拟类。我认为现在就说是否需要一般意义上的接口还为时过早。因此,我认为我们不应该把“尝试提供一个与所有抽象类的接口”作为一个编码标准,除非该编码标准包含更多关于何时应用该规则的细节C# 接口/抽象类编码标准,c#,.net,interface,abstract-class,coding-style,C#,.net,Interface,Abstract Class,Coding Style,我发现了一个提议中的C#编码标准,其中规定“尝试提供一个与所有抽象类的接口”。有人知道这样做的理由吗 如果不看原始文章,我猜原始作者是在建议它的可测试性,并允许使用MoQ、rhinomock等工具轻松模拟类。我认为现在就说是否需要一般意义上的接口还为时过早。因此,我认为我们不应该把“尝试提供一个与所有抽象类的接口”作为一个编码标准,除非该编码标准包含更多关于何时应用该规则的细节 如果我根本不打算使用接口,我还需要定义一个接口来满足编码标准吗?我一直理解接口驱动设计(IDD)在创建具体类时涉及以下
如果我根本不打算使用接口,我还需要定义一个接口来满足编码标准吗?我一直理解接口驱动设计(IDD)在创建具体类时涉及以下步骤(以最纯粹的形式,针对非平凡类):
virtual
)实现。您还可以提供适当的构造函数(在接口级别是不可能的)。将所有其他接口成员标记为抽象System.IO.Stream
抽象类为例。它最初不支持超时挂起的I/O操作,但2.0版能够添加支持此功能的成员,即使来自现有的子类
因此,为每个抽象基类提供相应的接口提供的额外好处很少。该接口不能公开公开,或者在版本控制方面,您只能原地踏步。如果只公开抽象基类,那么从一开始就没有什么好处
此外,这一点通常有利于接口,它们允许将契约与实现分离。Krzysztof Cwalina认为这种说法似是而非:它错误地假设您不能使用类将契约与实现分离。通过编写驻留在与其具体实现分离的程序集中的抽象类,很容易实现分离的相同优点。他写道:
我经常听到人们说接口指定合同。我相信这是一个危险的神话。接口本身并不指定使用对象所需的语法以外的内容。作为合同的接口神话导致人们在试图将合同与实现分离时做了错误的事情,这是一个伟大的工程实践。接口将语法与实现分开,这并不是很有用,而神话提供了正确工程的错误感觉。实际上,契约是语义的,通过一些实现可以很好地表达这些语义
一般来说,提供的指南是不支持定义类而不是接口。同样,Krzysztof评论:
在.NET Framework的三个版本的过程中,我与团队中的许多开发人员讨论了这条准则。他们中的许多人,包括那些最初不同意该准则的人,都表示他们后悔将一些API作为接口提供。我甚至没有听说过一个案例,在这个案例中,有人后悔他们提供了一个类
第二条准则认为,一个确实使用抽象类而不是接口来将契约与实现解耦。这里的要点是,正确设计的抽象类仍然允许契约和实现之间的解耦程度与接口相同。因此,布赖恩·佩平的个人观点是:
我已经开始做的一件事是在我的抽象类中实际烘焙尽可能多的契约。例如,我可能希望对一个方法使用四个重载,其中每个重载提供一组越来越复杂的参数。最好的方法是在抽象类上提供这些方法的非虚拟实现,并让实现全部路由到提供实际实现的受保护抽象方法。通过这样做,您可以一次性编写所有枯燥的参数检查逻辑。希望实现您的类的开发人员将感谢您
也许我们可以通过重温经常吹捧的“规则”来做得更好,即派生类表示与基类的IS-a关系,而实现接口的类与该接口有可以做的关系。要提出索赔,应始终对两个接口进行编码
// doesn't work - can't instantiate BaseClass directly
var target = new ClassForTesting(new BaseClass());
// where we only rely on interface can easily generate mock in our tests
var targetWithInterface = new ClassForTestingWithInterface(MockRepository.GenerateStub<ISomeInterface>());
// dependent class using an abstract class
public abstract class BaseClass
{
public abstract void SomeMethod();
}
public class ClassForTesting
{
public BaseClass SomeMember { get; private set; }
public ClassForTesting(BaseClass baseClass)
{
if (baseClass == null) throw new ArgumentNullException("baseClass");
SomeMember = baseClass;
}
}
public interface ISomeInterface
{
void SomeMethod();
}
public abstract class BaseClassWithInterface : ISomeInterface
{
public abstract void SomeMethod();
}
public class ClassForTestingWithInterface
{
public ISomeInterface SomeMember { get; private set; }
public ClassForTestingWithInterface(ISomeInterface baseClass) {...}
}