在《C#深入》中实例化抽象类

18

目前我正在阅读Jon Skeet的《深入理解C#》一书,其中有一个例子展示了使用抽象类实现接口并将其作为接口的附属类来进行代码合约。在Code Contracts术语中,这被称为“Contract Class For”(我不会在这里详细介绍Code Contracts的工作原理)。

接口(第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() { }

}

我的问题:

为什么需要在这个抽象类中添加私有构造函数,既然无法实例化抽象类?我哪里理解不正确?


确实令人困惑;抽象构造函数应该/可以是受保护的;难道这是为了让它更明确吗?尽管作为抽象类已经足够明确了...等待更多评论。 - Luis Filipe
11
不要质疑Jon Skeet的书籍! - Uwe Keim
2
我在质疑自己,而不是 Jon Skeet ... 因此出现了“我没有理解什么?” :-) - Aage
8
真希望 Jon Skeet 能回答这个问题... - MirroredFate
5个回答

14

抽象类无法直接实例化,但当它们被继承时,构造函数上的访问修饰符(例如私有(private))很重要。将构造函数设置为私有(private)而不是默认值,可以防止任何继承类被构造。由于这是唯一的构造函数,因此您有效地使类变为sealed,因为没有继承类(除非嵌套在ICaseConverterContracts中)可以编译(至少在C#中)。

我猜测Code Contracts代码通过反射或其他方式实例化该类,从而绕过了构造函数是私有(private)的问题。


4
除了关于反射部分的内容外,这句话是正确的。《Code Contracts》不会实例化该类,而是直接读取IL代码工具。 - Kris Vandermotten

7
将构造函数标记为私有的,可以防止任何派生类被实例化:
public class Foo : ICaseConverterContracts
{
    public Foo()  // does not compile as the base class constructor is inaccessible
    {
    }
}

这明确防止您在任何情况下实例化ICaseConverterContracts类,因为它不应该被实例化。


3
ContractClassFor是一个虚拟类,实现了接口,并发布了接口对其消费者的需求和承诺。从相应的ContractClass可以看出,接口及其合同类紧密耦合。(这种相当尴尬的实现可能是因为合同无法直接在接口上发布,因为接口上不允许有实现)。虚拟ContractClassFor中的合同随后将强制执行所有基础接口的“真实”实现(还要注意的是,只有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();
}

etc.


1
当您的合同类描述一个没有无参构造函数的类时,您需要指定构造函数。否则,它是完全不必要的。由于代码合同已经涉及了很多输入,建议您省略私有构造函数,以使内容更加通俗易懂。
私有构造函数会防止继承,因为派生类无法调用基类构造函数。
但是,由于代码合同重写器从您的代码中删除了所有这些类,所以在编译后的程序集中ICaseConverterContracts将不存在。
您的ICaseConverterContracts类仅会出现在合同程序集中的bin/Debug/CodeContracts/MyProject.Contracts.dll中。但是,该程序集仅供静态验证器使用:您永远不会直接使用它,甚至不会引用它。因此,在那里有一个私有构造函数也是不必要的。
我能想到唯一的原因是Jon Skeet在他的代码中包含它是为了向其他人表明该类不打算被实例化。

0

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

这种行为类似于sealed类,只不过该类仍然可以被嵌套类继承。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接