“深入的 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() { }
}
(我根据书中的注释在代码中添加了注释)
我的问题:
当您无法实例化抽象类时,为什么需要将私有构造函数添加到此抽象类中?我没有得到什么?
虽然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
类的行为,不同之处在于该类仍可由嵌套类继承。