Поиск

Позиционные и именованные параметры

В примере FieldAttrApp (см. выше) вы могли видеть атрибут с именем RegistryKeyAttribute. Его конструктор имел такой вид:

public RegistryKeyAttribute(RegistryHives Hive, String ValueName)
Далее к полю был прикреплен атрибут на основе сигнатуры этого конструктора:
[RegistryKey(RegistryHives.HKEY_CURRENT_USER, "Foo")] public int Foo;

Пока все просто. У конструктора есть два параметра, и два параметра использовались, чтобы прикрепить этот атрибут к полю. Однако мы можем упростить код. Если параметр большую часть времени останется неизменным, зачем каждый раз заставлять пользователя класса типизировать его? Мы можем установить значения по умолчанию, применив позиционные и именованные параметры.

Позиционными называются параметры конструктора атрибута. Они обязательны и должны задаваться каждый раз при использовании атрибута. В нашем примере Registry Key Attribute позиционными являются оба параметра, Hive и ValueName. Именованные параметры на самом деле не определяются в конструкторе атрибута. Они скорее представляют собой нестатические поля и свойства. Поэтому именованные параметры позволяют клиенту устанавливать значения полей и свойств атрибута при создании его экземпляра, не требуя от вас создания конструктора для каждой возможной комбинации полей и свойств, значения которых может понадобиться установить клиенту.

Каждый открытый конструктор может определять последовательность позиционных параметров. Это верно и в отношении любого типа класса. Но в случае атрибутов после указания позиционных параметров пользователь может ссылаться на некоторые поля или свойства, применяя синтаксис Имя_поля_или_свойства=3начение. Чтобы проиллюстрировать это, изменим атрибут Registry Key Attribute. Мы создадим лишь один позиционный параметр RegistryKeyAttribute. ValueName, а необязательным именованным параметром будет Registry KeyAttribute. Hive. Итак, возникает вопрос: "Как определить что-либо как именованный параметр?" Поскольку в определение конструктора включены только позиционные — и поэтому необходимые — параметры, просто удалите параметр из определения конструктора. Впоследствии пользователь может указывать как именованный параметр любое поле, не являющееся readonly, static или const, или любое поле, у которого есть метод-аксессор для установки его значения, или установщик, который не является статическим. Поэтому чтобы сделать Registry Key Attribute. Hive именованным параметром, мы уберем его из определения конструктора, так как он уже существует в виде открытого свойства, доступного для чтения и записи:

public RegistryKeyAttribute(String ValueName)
Теперь пользователь может прикрепить атрибут любым из следующих способов:
[RegistryKeyC'Foo")]
[RegistryKeyCToo", Hive = RegistryHives.HKEY_LOCAL_MACHINE)]

Это дает вам гибкость, обеспечиваемую наличием у поля значения по умолчанию, в то же время предоставляя пользователю возможность изменять это значение при необходимости. Секунду! Если пользователь не устанавливает значения поля Registry Key Attribute. Hive, как мы установим для него значение по умолчанию? Вы можете подумать: "Хорошо, посмотрим, не установлено ли оно в конструкторе". Однако проблема в том, что Registry KeyAttribute. Hive — это епит, в основе которого лежит int — размерный тип. Это значит, что по умолчанию компилятор инициализирует его значением 0! Если мы изучим значение RegistryKey-Attribute.Hive в конструкторе и найдем его равным 0, мы не сможем узнать, установлено ли оно вызывающим кодом через именованный параметр или инициализировано компилятором как размерный тип. К сожалению, единственный известный мне пока способ решения этой проблемы — это изменить код так, чтобы значение, равное 0, стало неверным. Это можно сделать, изменив RegistryHives епит:

public enum RegistryHives {
HKEY_CLASSES_ROOT = 1,
HKEY_CURRENT_USER,
HKEY_LOCAL_MACHINE,
HKEY_USERS,
HKEY_CURRENT_CONFIG }

Теперь мы знаем, что единственный способ, позволяющий Registry-Key Attribute. Hive быть равным 0, — инициализация его компилятором этим значением, если после этого пользователь не изменил его значение через именованный параметр. Сейчас мы можем написать для инициализации код вроде этого:

public RegistryKeyAttribute(String ValueName) <
if (this.Hive == 0)
this.Hive = RegistryHives.HKEY_CURRENT_USER;
this.ValueName = ValueName; }