Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 接口/抽象类编码标准_C#_.net_Interface_Abstract Class_Coding Style - Fatal编程技术网

C# 接口/抽象类编码标准

C# 接口/抽象类编码标准,c#,.net,interface,abstract-class,coding-style,C#,.net,Interface,Abstract Class,Coding Style,我发现了一个提议中的C#编码标准,其中规定“尝试提供一个与所有抽象类的接口”。有人知道这样做的理由吗 如果不看原始文章,我猜原始作者是在建议它的可测试性,并允许使用MoQ、rhinomock等工具轻松模拟类。我认为现在就说是否需要一般意义上的接口还为时过早。因此,我认为我们不应该把“尝试提供一个与所有抽象类的接口”作为一个编码标准,除非该编码标准包含更多关于何时应用该规则的细节 如果我根本不打算使用接口,我还需要定义一个接口来满足编码标准吗?我一直理解接口驱动设计(IDD)在创建具体类时涉及以下

我发现了一个提议中的C#编码标准,其中规定“尝试提供一个与所有抽象类的接口”。有人知道这样做的理由吗

如果不看原始文章,我猜原始作者是在建议它的可测试性,并允许使用MoQ、rhinomock等工具轻松模拟类。

我认为现在就说是否需要一般意义上的接口还为时过早。因此,我认为我们不应该把“尝试提供一个与所有抽象类的接口”作为一个编码标准,除非该编码标准包含更多关于何时应用该规则的细节


如果我根本不打算使用接口,我还需要定义一个接口来满足编码标准吗?

我一直理解接口驱动设计(IDD)在创建具体类时涉及以下步骤(以最纯粹的形式,针对非平凡类):

  • 创建一个界面来描述对象必须展示的属性和行为,而不是这些属性和行为应该如何发挥作用
  • 创建一个抽象基类作为接口的主要实现。实现接口所需的任何功能,但具体实现之间不太可能存在差异。还为不太可能(但可能)更改的成员提供适当的默认(
    virtual
    )实现。您还可以提供适当的构造函数(在接口级别是不可能的)。将所有其他接口成员标记为抽象
  • 从抽象类创建具体类,覆盖最初由接口定义的成员子集
  • 上面的过程虽然冗长,但可以确保最大程度地遵守最初制定的契约,同时最小化替代实现中的冗余代码

    这就是我通常将抽象类与接口配对的原因。

    关于接口和抽象类,我有一些有趣的事情要说

    特别是,他们注意到接口的主要缺点是在API的演化方面不如类灵活。一旦您发布了一个接口,它的成员将永远固定,任何添加都将破坏与实现该接口的现有类型的兼容性。然而,发布类提供了更大的灵活性。成员可以随时添加,即使在初始版本发布之后,只要它们不是抽象的。任何现有的派生类都可以不加更改地继续工作。以框架中提供的
    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) {...}
    }