Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ООП C#.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
13.94 Mб
Скачать

Еще раз о необязательных аргументах

Вспомните, что необязательные аргументы позволяют определять стандартные значения для входных аргументов. Если вызывающий код удовлетворяют эти стандартные значения, указывать уникальные значения не обязательно, но это можно делать, когда объект требуется снабдить специальными данными. Рассмотрим следующую версию класса Motorcycle, который теперь предоставляет несколько возможностей конструирования объектов, используя единственное определение конструктора.

С помощью этого единственного конструктора можно создать объект Motorcycle, указывая ноль, один или два аргумента.

Хотя применение необязательных/именованных аргументов — очень удобный путь упрощения определения набора конструкторов, используемых заданным классом, следует всегда помнить, что этот синтаксис является допустимым только в .NET 4.0 или последующих версиях. Если требуется строить классы, которые должны выполняться на платформе .NET любой версии, лучше придерживаться классической техники цепочек конструкторов.

Реализуйте в вашем классе конструктор с необязательными параметрами

Понятие ключевого слова static

Класс C# может определять любое количество статических членов, которые объявляются с использованием ключевого слова static. При этом соответствующий член должен вызываться непосредственно на уровне класса, а не на переменной, хранящей ссылку на объект. Чтобы проиллюстрировать разницу, обратимся к System.Console. Как уже было показано, метод WriteLine() не вызывается на уровне объекта:

Вместо этого статический член WriteLine() предваряется именем класса:

Проще говоря, статические члены — это элементы, задуманные (проектировщиком класса) как общие, так что нет нужды создавать экземпляр класса перед их вызовом. Хотя в любом классе можно определять статические члены, чаще всего их можно обнаружить внутри “обслуживающих классов”. По определению обслуживающий класс — это такой класс, который поддерживает состояние на уровне объектов и не создается посредством ключевого слова new. Вместо этого обслуживающий класс открывает всю функциональность в виде членов уровня класса (т.е. статических). Например, если воспользоваться браузером объектов Visual Studio (выбрав пункт меню View→Object Browser (Вид→Браузер объектов)) для просмотра пространства имен System из сборки mscorlib.dll, можно увидеть, что все члены классов Console, Math, Environment и GC (и ряд других) открывают свою функциональность через статические члены. Это лишь несколько обслуживающих классов, определенных в библиотеках базовых классов .NET.

Опять-таки, следует помнить, что статические члены могут находиться не только в обслуживающих классах; они могут быть частью любого определения класса. Просто не забывайте, что статические члены перемещают заданный элемент на уровень класса, а не объекта.

Определение статических полей данных

Большую часть времени при проектировании класса данные определяются на уровне экземпляра; говоря иначе, это нестатические данные. Когда определяются данные уровня экземпляра, известно, что при каждом создании нового объекта этот объект поддерживает собственную независимую копию таких данных. В противоположность этому, если определены статические данные класса, эта память разделяется всеми объектами соответствующей категории. Чтобы увидеть разницу, создайте новый проект консольного приложения по имени StaticDataAndMembers и вставим в него новый класс под названием SavingsAccount.

Начнем с определения элемента данных уровня экземпляра (для моделирования текущего баланса) и специального конструктора для установки начального баланса:

При создании объектов SavingsAccount память под поле currBalance выделяется для каждого объекта. Таким образом, можно было бы создать пять разных объектов SavingsAccount, каждый с собственным уникальным балансом. Более того, если вы измените баланс на каком-нибудь одном счету, другие объекты не будут затронуты.

С другой стороны, статические данные распределяются однажды и разделяются всеми объектами того же самого класса. Добавьте в класс SavingsAccount статический элемент данных по имени currInterestRate, принимающий стандартное значение 0.04:

Если создать три экземпляра SavingsAccount, как показано ниже:

то размещение данных в памяти будет выглядеть примерно так, как показано на рис. 23.

Рис. 23. Статические данные размещаются один раз и разделяются между всеми экземплярами класса

Здесь мы предполагаем, что все депозитные счета должны иметь одну и ту же процентную ставку. Поскольку статические данные разделяются всеми объектами той же самой категории, если вы измените процентную ставку каким-либо образом, то все объекты будут “видеть” новое значение при следующем доступе к статическим данным, поскольку все они, в сущности, просматривают одно и то же местоположение в памяти. Чтобы понять, как изменять (или получать) статические данные, понадобится рассмотреть роль статических методов. Определение статических методов Измените класс SavingsAccount, добавив к нему два статических метода. Первый статический метод (GetInterestRate()) будет возвращать текущую процентную ставку, а второй (SetInterestRate())позволит изменять процентную ставку:

Рассмотрим следующий сценарий использования класса:

Вывод предыдущего метода Main() показан ниже:

Как видите, при создании новых экземпляров класса SavingsAccount значение статических данных не сбрасывается, поскольку CLR выделяет для них место в памяти только один раз. После этого все объекты типа SavingsAccount оперируют одним и тем же значением в статическом поле currInterestRate. При проектировании любого класса C# одна из задач связана с выяснением того, какие части данных должны быть определены как статические члены, а какие — нет. Хотя на этот счет не существует строгих правил, помните, что поле статических данных разделяется между всеми объектами конкретного класса. Поэтому, если необходимо, чтобы часть данных совместно использовалась всеми объектами, то статические члены будут самым подходящим вариантом. Определение статических конструкторов

Типичный конструктор используется для установки значений данных уровня экземпляра в объекте во время его создания. Однако что случится, если вы попытаетесь присвоить значение статическому элементу данных в типичном конструкторе? Вас может удивить, когда обнаружится, что это значение сбрасывается каждый раз, когда создается новый объект! В целях иллюстрации предположим, что конструктор класса SavingsAccount изменен, как показано ниже (также обратите внимание, что поле currInterestRate больше не устанавливается при объявлении):

Теперь рассмотрим следующий код в методе Main():

При выполнении предыдущего метода Main() обнаруживается, что переменная currInterestRate будет сбрасываться при каждом создании нового объекта SavingsAccount, всегда возвращаясь к значению 0.04. Ясно, что установка значений статических данных в нормальном конструкторе уровня экземпляра сводит на нет весь их смысл. Всякий раз, когда создается новый объект, данные уровня класса сбрасываются! Один из способов правильной установки статического поля состоит в использовании синтаксиса инициализации члена, как это делалось изначально:

Этот подход гарантирует, что статическое поле будет установлено только однажды, независимо от того, сколько объектов будет создано. Однако что, если значение статических данных нужно получить во время выполнения? Например, в типичном банковском приложении значение переменной, представляющей процентную ставку, должно быть прочитано из базы данных или внешнего файла. Решение подобных задач требует контекста метода (такого как конструктор), чтобы можно было выполнить операторы кода. Именно по этой причине в C# предусмотрена возможность определения статического конструктора, который позволяет безопасно устанавливать значения статических данных. Взгляните на следующее изменение в классе:

Выражаясь упрощенно, статический конструктор — это специальный конструктор, который является идеальным местом для инициализации значений статических данных, когда их значение не известно на момент компиляции (например, когда его нужно прочитать из внешнего файла, базы данных, сгенерировать случайное число или еще каким-то образом получить значение). Если запустить заново предыдущий метод Main(), вы увидите ожидаемый вывод. Обратите внимание, что сообщение "In static ctor!" выводится только один раз, поскольку среда CLR вызывает все статические конструкторы перед первым использованием (и никогда не вызывает их повторно для этого экземпляра приложения):

Ниже приведено несколько интересных моментов, касающихся статических конструкторов.

  • В отдельном классе может быть определен только один статический конструктор. Другими словами, статический конструктор нельзя перегружать.

  • Статический конструктор не имеет модификатора доступа и не может принимать параметров.

  • Статический конструктор выполняется только один раз, независимо от того, сколько объектов отдельного класса создается.

  • Исполняющая система вызывает статический конструктор, когда создает экземпляр класса или перед первым обращением к статическому члену этого класса.

  • Статический конструктор выполняется перед любым конструктором уровня экземпляра.

  • Учитывая сказанное, при создании новых объектов SavingsAccount значения статических данных сохраняются, поскольку статический член устанавливается только один раз внутри статического конструктора, независимо от количества созданных объектов.

Реализуйте статический конструктор для класса SavingsAccount