Подтвердить что ты не робот

Абстрактный экземпляр класса в 'С# in depth'

В настоящее время я читаю "С# in depth" от Jon Skeet и там пример, изображающий Code Contracts с абстрактным классом, реализующим интерфейс, который в качестве сопутствующего класса для интерфейса, в терминах 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() { }

}

(Я добавил комментарии в код, основанный на комментариях в книге)

Мой вопрос:

Почему необходимо добавить частный конструктор в этот абстрактный класс, если вы не можете создать экземпляр абстрактного класса для начала? Что я не получаю?

4b9b3361

Ответ 1

Хотя классы abstract не могут быть непосредственно созданы, модификатор доступа (например, private) в конструкторе имеет значение, когда они наследуются. Создав конструктор private вместо значения по умолчанию, вы делаете его так, чтобы ни один унаследованный класс не мог быть сконструирован. Поскольку это единственный конструктор, вы эффективно делаете класс sealed, так как не наследующий класс (если он не вложен в ICaseConverterContracts) не может скомпилироваться (в С#, по крайней мере).

Я предполагаю, что код Code Contracts создает экземпляр класса посредством отражения или каким-либо другим способом, который обходит проблему конструктора private.

Ответ 2

Отмечая, что конструктор как закрытый не позволяет создавать экземпляр любого производного класса:

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

Это явно запрещает вам создавать экземпляр класса ICaseConverterContracts при любых обстоятельствах, поскольку это не класс, который должен быть создан.

Ответ 3

ContractClassFor - это реализация фиктивного класса для интерфейса, без какой-либо иной цели, чем публикация контрактов, требуемых интерфейсом, и promises для своих потребителей. Как видно из соответствующего 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();
}

и др.

Ответ 4

Вам нужно указать конструктор, когда класс контракта описывает класс, у которого нет конструктора без параметров. В противном случае это совершенно не нужно. И поскольку в Code Contracts уже есть много ввода, я предлагаю вам оставить частный конструктор.

Частный конструктор предотвращает наследование, поскольку производный класс не может вызвать конструктор базового класса.

Но поскольку Code Rewriter сокращает все эти классы из вашего кода. ICaseConverterContracts не будет существовать в скомпилированной сборке.

Единственное место, где будет отображаться ваш класс ICaseConverterContracts, находится в сборке контрактов в bin/Debug/CodeContracts/MyProject.Contracts.dll. Но эта сборка предназначена только для статического верификатора: вы никогда не будете использовать ее напрямую или даже имеете ссылку на нее. Так что наличие частного конструктора там также не требуется.

Единственная причина, по которой я могу думать о том, почему Джон Скит включил его в свой код, - это сигнал другим людям, читающим код, который не предназначен для создания экземпляра класса.

Ответ 5

Наличие частного конструктора означает, что вы не можете наследовать этот класс.

Поведение похоже на поведение класса sealed, за исключением того, что класс все еще может быть унаследован вложенными классами.