简体   繁体   English

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

[英]Abstract class instantiation in 'C# in depth'

I'm reading 'C# in depth' by Jon Skeet currently and there's an example depicting Code Contracts with an abstract class implementing an interface which features as an accompanying class for the interface, in Code Contracts' terms: a 'Contract Class For' (I'm not going into details about the workings of Code Contracts here). 我正在阅读Jon Skeet目前正在阅读的“C#深度”,并且有一个示例描述代码合同,其中一个抽象类实现了一个接口,该接口具有作为接口的附带类,代码合同的术语:'Contract Class For'(我不会在这里详细介绍代码合同的运作方式。

The interface (p. 467): 界面(第467页):

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

The abstract class: 抽象类:

[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() { }

}

(I've added the comments in the code based on comments in the book) (我根据书中的评论在代码中添加了注释)

My question: 我的问题:

Why is it necessary to add the private constructor to this abstract class when you can't instantiate an abstract class to begin with? 当你无法实例化一个抽象类时,为什么有必要将私有构造函数添加到这个抽象类? What am I not getting? 我得不到什么?

While abstract classes can't be directly instantiated, the access modifier (eg private ) on the constructor matters when they are inherited. 虽然abstract类无法直接实例化,但构造函数上的访问修饰符(例如private )在继承时很重要。 By making the constructor private instead of the default, you are making it so that no inherited class can be constructed. 通过使构造函数为private而不是默认值,您可以创建它,以便不能构造任何继承的类。 Since this is the only constructor, you are effectively making the class sealed , since no inheriting class (unless it's nested in ICaseConverterContracts ) can compile (in C#, at least). 由于这是唯一的构造函数,因此您无法有效地使类sealed ,因为没有继承类(除非它嵌套在ICaseConverterContracts )可以编译(至少在C#中)。

I'm guessing that the Code Contracts code instantiates the class via reflection, or some other way that bypasses the problem of the constructor being private . 我猜测Code Contracts代码通过反射实例化类,或者绕过构造函数private其他方式。

Marking that constructor as private prevents any derived class from being instantiated, too: 将构造函数标记为私有也会阻止任何派生类被实例化:

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

This explicitly prevents you from instantiating the ICaseConverterContracts class under any circumstance, as it's not a class that should ever be instantiated. 这明确阻止您在任何情况下实例化ICaseConverterContracts类,因为它不是应该实例化的类。

The ContractClassFor is a dummy class implementation for the interface, with no other purpose than to publish the contracts that the interface demands from, and promises to, its consumers. ContractClassFor是接口的虚拟类实现,除了发布接口对其消费者要求和承诺的合同之外没有其他目的。 As can be seen from the corresponding ContractClass , the interface and its contract class are tightly coupled. 从相应的ContractClass可以看出,接口及其契约类是紧密耦合的。 (This rather awkward implementation is presumably is because the contracts can't be published directly on the interface, as there is no implementation allowed on an interface). (这个相当尴尬的实现可能是因为合同无法直接在接口上发布,因为接口上不允许实现)。 The contracts in the dummy ContractClassFor are then enforced for all real implementations of the underlying interface (Also note that only the ContractClassFor dummy implementation is able to publish contracts for the interface - otherwise different implementations could have different contracts, which wouldn't really make sense.) 然后对底层接口的所有real实现强制实施伪ContractClassFor中的ContractClassFor (另请注意,只有ContractClassFor虚拟实现能够发布接口的合同 - 否则不同的实现可能会有不同的合同,这实际上没有意义。)

The ContractClassFor class is never instantiated, and you will often find dummy implementations just to get the compiler to compile, eg ContractClassFor类永远不会被实例化,你经常会发现虚拟实现只是为了让编译器进行编译,例如

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

or 要么

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

etc. 等等

You need to specify a constructor when your contract class describes a class that doesn't have a parameterless constructor. 当您的契约类描述没有无参数构造函数的类时 ,您需要指定构造函数。 Otherwise, it is completely unnecessary. 否则,完全没必要。 And since Code Contracts already involve a lot of typing, I suggest you leave the private constructor out . 由于Code Contracts已经涉及很多打字, 我建议你不要使用私有构造函数

The private constructor prevents inheritance, as the derived class cannot call the base class constructor. 私有构造函数阻止继承,因为派生类无法调用基类构造函数。

But since the Code Contract Rewriter removes all these classes from your code. 但是,由于Code Contract Rewriter从您的代码中删除了所有这些类。 ICaseConverterContracts will not exist in the compiled assembly. ICaseConverterContracts 将不存在于已编译的程序集中。

The only place where your ICaseConverterContracts class will show up is in the contracts assembly in bin/Debug/CodeContracts/MyProject.Contracts.dll . 您的ICaseConverterContracts类将显示的唯一位置是bin/Debug/CodeContracts/MyProject.Contracts.dll中的contract程序集。 But that assembly is only for the static verifier: you'll never use it directly, or even have a reference to it. 但是该程序集仅适用于静态验证程序:您永远不会直接使用它,甚至不会引用它。 So having a private constructor in there is also not required. 因此,在那里拥有私有构造函数也不是必需的。

The only reason I can think of why Jon Skeet included it in his code is to signal to other people reading the code that the class is not meant to be instantiated. 我能想到为什么Jon Skeet将其包含在他的代码中的唯一原因是向其他人发出信号,告诉他们该类不是要实例化的代码。

Having a private constructor means you can't inherit from that class. 拥有私有构造函数意味着您无法从该类继承。

The behavior is similar to that of a sealed class, except the class can still be inherited by nested classes. 该行为类似于sealed类的行为,除了该类仍然可以由嵌套类继承。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM