18

我目前正在阅读 Jon Skeet 的“深入 C#”,并且有一个示例描述了代码契约,其中抽象类实现了一个接口,该接口以代码契约的术语作为接口的伴随类:“契约类”(我不会在这里详细介绍代码合同的工作原理)。

界面(第 467 页):

[ContractClass(typeof(ICaseConverterContracts))]
public interface ICaseConverter
{
    string Convert(string text);
}

抽象类:

[ContractClassFor(typeof(ICaseConverter))]
internal abstract class ICaseConverterContracts : ICaseConverter
{
    public string Convert(string text)
    {
         Contract.Requires(text != null);
         Contract.Ensures(Contract.Result<string>() != null);
         return default(string); // returns dummy value
    }

    // prevents instantiation
    private ICaseConverterContracts() { }

}

(我根据书中的注释在代码中添加了注释)

我的问题:

既然不能实例化一个抽象类,为什么还要给这个抽象类添加私有构造函数呢?我没有得到什么?

4

5 回答 5

14

虽然abstract类不能直接实例化,private但构造函数上的访问修饰符(例如 )在它们被继承时很重要。通过使用构造函数private而不是默认构造函数,您可以构造任何继承的类。由于这是唯一的构造函数,因此您实际上是在创建 class sealed,因为没有继承类(除非它嵌套在 中ICaseConverterContracts)可以编译(至少在 C# 中)。

我猜代码合同代码通过反射或其他绕过构造函数问题的方式实例化类private

于 2013-10-22T13:58:15.447 回答
7

将该构造函数标记为私有也可以防止任何派生类被实例化:

public class Foo : ICaseConverterContracts
{
    public Foo()  // does not compile as the base class constructor is inaccessible
    {
    }
}

这明确地阻止您ICaseConverterContracts在任何情况下实例化该类,因为它不是一个应该被实例化的类。

于 2013-10-22T13:58:40.440 回答
3

是接口的ContractClassFor一个虚拟类实现,除了发布接口要求和承诺给它的消费者的合同外,没有其他目的。从对应ContractClass的 中可以看出,接口和它的契约类是紧耦合的。(这个比较尴尬的实现大概是因为合约不能直接在接口上发布,因为接口上不允许实现)。ContractClassFor然后为底层接口的所有实现强制执行虚拟中的合同real(另请注意,只有ContractClassFor虚拟实现才能发布接口的合同 - 否则不同的实现可能有不同的合同,这没有任何意义。)

该类ContractClassFor永远不会被实例化,并且您经常会发现虚拟实现只是为了让编译器进行编译,例如

public string Convert(string text)
{
     Contract.Requires(text != null);
     Contract.Ensures(Contract.Result<string>() != null);
     return default(string); // returns dummy value
}

或者

public string Convert(string text)
{
     Contract.Requires(text != null);
     Contract.Ensures(Contract.Result<string>() != null);
     throw new NotImplementedException();
}

等等

于 2013-10-22T13:57:04.857 回答
1

当您的合约类描述一个没有无参数构造函数的类时,您需要指定一个构造函数。否则,完全没有必要。而且由于代码契约已经涉及大量的输入,我建议你将私有构造函数排除在外

私有构造函数阻止继承,因为派生类不能调用基类构造函数。

但是由于代码契约重写器从您的代码中删除了所有这些类。ICaseConverterContracts 不会存在于已编译的程序集中。

ICaseConverterContracts您的班级将出现的唯一地方是bin/Debug/CodeContracts/MyProject.Contracts.dll. 但是该程序集仅适用于静态验证器:您永远不会直接使用它,甚至不会引用它。所以也不需要有一个私有构造函数。

我能想到 Jon Skeet 将它包含在他的代码中的唯一原因是向阅读代码的其他人发出信号,表明该类不应该被实例化。

于 2013-10-22T17:26:37.920 回答
0

拥有私有构造函数意味着您不能从该类继承。

其行为类似于sealed类的行为,不同之处在于该类仍然可以被嵌套类继承。

于 2013-10-22T14:05:46.517 回答