“深入的 C#”中的抽象类实例化

本文关键字:抽象类 实例化 深入的 | 更新日期: 2023-09-27 18:36:28

我目前正在阅读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() { }
}

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

我的问题:

当您无法实例化抽象类时,为什么需要将私有构造函数添加到此抽象类中?我没有得到什么?

“深入的 C#”中的抽象类实例化

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

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

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

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

这显式阻止您在任何情况下实例化 ICaseConverterContracts 类,因为它不是应该实例化的类。

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();
}

等。

当合约类描述没有无参数构造函数的时,您需要指定构造函数。否则,完全没有必要。由于代码契约已经涉及大量类型,我建议你省略私有构造函数

私有构造函数

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

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

您的ICaseConverterContracts类将出现的唯一位置是 bin/Debug/CodeContracts/MyProject.Contracts.dll 中的合约程序集。但该程序集仅适用于静态验证程序:您永远不会直接使用它,甚至不会引用它。因此,也不需要在其中使用私有构造函数。

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

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

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