Конструктори в класах атрибутів. Позиційні та іменовані параметри. Приклади
Перед вивченням даної теми рекомендується ознайомитись з наступною темою:
Зміст
- 1. Конструктори в класах атрибутів. Особливості застосування. Приклад
- 2. Позиційні та іменовані параметри. Визначення. Приклад
- 3. Приклад використання іменованого параметру
- 4. Приклад використання позиційних та іменованих параметрів у класі-атрибуті. Конструктор отримує два параметри
- 5. Застосування атрибуту до методу. Приклад
- Зв’язані теми
Пошук на інших ресурсах:
1. Конструктори в класах атрибутів. Особливості застосування. Приклад
Як правило, класи атрибутів містять невелику кількість внутрішніх полів (даних). Ці внутрішні поля ініціалізуються з допомогою конструкторів. При приєднанні класів атрибутів до інших класів потрібно дотримуватись правильного задавання параметрів цих конструкторів.
Наприклад, якщо клас атрибуту має конструктор з параметрами типу string та int
// Деякий клас атрибуту class MyClassCommentAttribute : Attribute { // Внутрішні поля string field1; int field2; // Конструктор з параметрами типу string, int public MyClassCommentAttribute(string _param1, int _param2) { // ... } }
то при приєднанні цього класу до іншого класу потрібно дотримуватись правильного порядку та сигнатури в параметрах
... [MyClassComment("Some comment", 5)] class MyClass { // ... } ...
У вищенаведеному коді в рядку
[MyClassComment("Some comment", 5)]
викликається конструктор класу MyClassCommentAttribute. Конструктор отримує першим параметром рядок “Some comment”, другим параметром цілочисельне значення 5. Якщо спробувати змінити кількість або типи значень, що передаються в конструктор, виникне помилка на етапі компіляції.
⇑
2. Позиційні та іменовані параметри. Визначення. Приклад
У конструкторах класів-атрибутів розрізняють два види параметрів:
- позиційні параметри. У цих параметрах аргумент зв’язується з параметром відповідно до його позиції в списку аргументів. Якщо в конструктор класу-атрибуту передається декілька позиційних аргументів, то перший аргумент зв’язується з першим параметром, другий аргумент зв’язується з другим параметром і т.д. Як правило, позиційні параметри ініціалізують внутрішні поля класу атрибуту;
- іменовані параметри. Це випадок, коли параметру присвоюється початкове значення за його іменем. У цьому випадку важливим є ім’я параметру, а не його позиція. Іменованими параметрами можуть бути тільки загальнодоступні (public) поля або властивості класу-атрибуту.
У найбільш загальному випадку, оголошення класу атрибуту, що містить конструктор з позиційними параметрами, може бути таким:
class NameOfClassAttribute : Attribute { ... // Конструктор, в якому параметри передаються за їх позиціями public NameOfClassAttribute( type1 pos_parameter1, type2 pos_parameter2, ... typeN pos_parameterN) { ... } ... }
Після того, як в класі атрибуту визначено конструктор що містить перелік позиційних параметрів, цей клас може бути приєднаний до будь-якого іншого класу за наступною загальною формою:
[NameOfClassAttribute( pos_parameter1, pos_parameter2, ... pos_parameterN, named_parameter_1 = value1, named_parameter_2 = value2, ... named_parameter_N = valueN)] class SomeClass { ... }
тут
- NameOfClassAttribute – клас атрибуту, що приєднується до іншого класу з іменем SomeClass;
- pos_parameter1, pos_parameter2, pos_parameterN – імена позиційних параметрів;
- named_parameter1, named_parameter2, named_parameterN – імена іменованих параметрів, яким присвоюються відповідно значення value1, value2, valueN.
⇑
3. Приклад використання іменованого параметру
Нехай задано клас Car, що описує інформацію про автомобіль. До класу приєднується клас-атрибут CommentAttribute, який додає до класу деяке пояснення (коментар).
Реалізація класу Car містить наступні елементи:
- внутрішні поля model та year, які визначають марку автомобіля та рік випуску;
- конструктор з двома параметрами;
- властивості Model та Year, які повертають значення внутрішніх полів.
Клас CommentAttribute успадкований з класу AttributeUsage, який обмежує приєднання CommentAttribute тільки до класів. У класі CommentAttribute реалізовано наступні елементи:
- загальнодоступне (public) поле comment, яке містить коментар. При приєднанні до класу Car, це поле буде задаватись як іменований параметр;
- конструктор без параметрів. У тілі конструктора ініціалізується поле comment значенням за замовчуванням.
Клас CommentAttribute може бути приєднаний до будь-яких інших класів.
У функції main() з допомогою рефлексії виводиться інформація про приєднані до класу Car атрибути.
Текст демонстраційної програми наступний:
using System; using System.Reflection; namespace ConsoleApp18 { // 1. Клас-атрибут, який визначає загальнодоступний коментар [AttributeUsage(AttributeTargets.Class)] // використати тільки для класів class CommentAttribute : Attribute { public string comment; // Конструктор без параметрів public CommentAttribute() { comment = "No comments."; // значення за замовчуванням } } // 2. Клас, що описує дані про автомобіль. // До класу приєднується клас CarAttribute зі значенням коментаря "This is a car". [Comment(comment = "This is a car!")] // тут comment - іменований параметр class Car { // Внутрішні поля класу private string model; private int year; // Конструктор public Car(string _model, int _year) { model = _model; year = _year; } // Властивості доступу до полів public string Model { get { return model; } } public int Year { get { return year; } } } class Program { static void Main(string[] args) { // Демонстрація використання // 1. Створити екземпляр класу Car Car car = new Car("Renault", 2008); // 2. Вивести інформацію про приєднаний атрибут CommentAttribute object[] attrs = Attribute.GetCustomAttributes(car.GetType()); foreach (object at in attrs) { // 2.1. Вивести ім'я приєднаного атрибуту на основі екземпляру at Console.WriteLine("Attribute name: " + at.GetType().Name); // 2.2. Вивести значення коментарію, який приєднаний до класу Car // 2.2.1. Отримати екземпляр типу Type Type tp = at.GetType(); // 2.2.2. Отримати інформацію про поле comment FieldInfo fi = tp.GetField("comment"); // 2.2.3. Отримати значення поля comment string comment = (string)fi.GetValue(at); // 2.2.4. Вивести comment на екран Console.WriteLine("comment = " + comment); // This is a car! } Console.ReadKey(); } } }
Після запуску на виконання програма видасть наступний результат
Attribute name: CommentAttribute comment = This is a car!
Пояснимо деякі фрагменти коду. Клас-атрибут створюється в рядках
... [AttributeUsage(AttributeTargets.Class)] // використати тільки для класів class CommentAttribute : Attribute { ... } ...
Як видно з вищенаведеного коду, клас успадкований від вбудованого класу AttributeUsage. Цей клас дозволяє обмежити застосування користувацьких класів-атрибутів до певних елементів. У нашому випадку в конструкторі класу AttributeUsage вказується значення AttributeTargets.Class. Це означає, що наш клас CommentAttribute може бути приєднаний тільки до класів. До інших елементів (структури, зчислення, методи тощо) він не може бути приєднаний.
Приєднання класу-атрибуту CommentAttribute до класу Car здійснюється в рядках
... [Comment(comment = "This is a car!")] // тут comment - іменований параметр class Car { ... } ...
Як видно з рядків вище, не обов’язково вказувати повне ім’я класу CommentAttribute. Достатньо вказати тільки ім’я Comment. Однак, якщо вказати CommentAttribute, то помилки не буде.
При приєднанні, викликається конструктор класу CommentAttribute. У даному випадку, задається конкретне значення іменованого параметру comment
Comment(comment = "This is a car!")
Це означає, що клас CommentAttribute приєднується до класу автомобіля.
Існує й інший спосіб приєднання класу-атрибуту. При цьому способі не обов’язково вказувати іменований параметр. Приєднання може бути реалізоване значенням за замовчуванням наступним чином
[Comment] // не задано іменованого параметру class Car { ... }
або
[Comment()] class Car { ... }
У цьому випадку внутрішнє поле comment буде ініціалізовано значенням за замовчуванням “No comments.” яке задається в конструкторі класу CommentAttribute
... class CommentAttribute : Attribute { ... // Конструктор без параметрів public CommentAttribute() { comment = "No comments."; // значення за замовчуванням } }
⇑
4. Приклад використання позиційних та іменованих параметрів у класі-атрибуті. Конструктор отримує два параметри
У прикладі оголошується клас з іменем SomeClassAttribute. У класі оголошуються наступні елементи:
- внутрішні приховані (private) поля з іменами fInt та fDouble;
- загальнодоступні (public) поля з іменами fString та fFloat;
- конструктор, який отримує два позиційні параметри.
З метою демонстрації клас SomeClassAttribute приєднується до чотирьох класів з іменами Class1, Class2, Class3, Class4 різними способами:
- без вказання іменованих параметрів (Class1);
- з вказання одного іменованого параметру (Class2, Class3);
- з вказанням двох іменованих параметрів (Class4).
... // Деякий клас, що містить конструктор, // який отримує два позиційні параметри class SomeClassAttribute : Attribute { // Внутрішні приховані поля класу private int fInt; private double fDouble; // Загальнодоступні поля класу public string fString; public float fFloat; // Конструктор, отримує два позиційні параметри public SomeClassAttribute( int param1, double param2) { // Ініціалізація прихованих полів значеннями позиційних параметрів fInt = param1; fDouble = param2; // Ініціалізація загальнодоступних полів у конструкторі // значеннями за замовчуванням fString = "None"; fFloat = 1.0f; } } // Приєднання класу NameOfClassAttribute до іншого класу. // Випадок 1 - не вказуються іменовані параметри [SomeClass(25, 8.83)] class Class1 { // ... } // Випадок 2 - вказується тільки один іменований параметр типу string [SomeClassAttribute(3, 7.55, fString = "SomeClass2")] class Class2 { // ... } // Випадок 3 - вказується тільки один іменований параметр типу float [SomeClass(8, 1.25, fFloat = 9.5f)] class Class3 { // ... } // Випадок 4 - вказуються два іменовані параметри [SomeClass(1, -10.5, fFloat = 1.8f, fString = "SomeClass3")] class Class4 { // ... } ...
⇑
5. Застосування атрибуту до методу. Приклад
Атрибути можуть приєднуватись до методів класу. Нижче наведено приклад приєднання атрибуту VersionAttribute до методу Print() класу CharArray. Атрибут визначає номер версії для методу Print().
У функції main() виводяться дані про приєднаний атрибут:
- назва класу-атрибуту;
- значення поля екземпляру, яке є номером версії методу.
using System; using System.Reflection; namespace ConsoleApp18 { // 1. Оголосити клас, що є атрибутом. // Клас описує рядок, що визначає версію елементу. // Цей клас-атрибут використовується тільки для методу. [AttributeUsage(AttributeTargets.Method)] class VersionAttribute : Attribute { // 1.1. Загальнодоступне поле - версія методу public string version; // 1.2. Конструктор без параметрів public VersionAttribute() { version = "None"; // значення за замовчуванням } // 1.3. Властивість для доступу до версії public string Version { get { return version; } } } // 2. Клас, що описує масив символів типу char[]. // У класі є метод Print(), до якого приєднується атрибут VersionAttribute class CharArray { // 2.1. Внутрішнє поле private char[] ca; // 2.2. Конструктор public CharArray(char[] _ca) { ca = _ca; } // 2.3. Метод Print(), до якого приєднується атрибут VersionAttribute [Version(version = "1.0")] // version - іменований параметр public void Print() { Console.WriteLine("Array CA:"); for (int i = 0; i < ca.Length; i++) Console.Write(ca[i] + " "); Console.WriteLine(); } // 2.4. Властивість доступу до поля ca public char[] Value { get { return ca; } } } class Program { static void Main(string[] args) { // 1. Використання класу CharArray // 1.1. Оголосити тестувальний масив char[] A = { 'a', 'c', 'f', '=', '+' }; // 1.2. Створити екземпляр типу CharArray CharArray CA = new CharArray(A); // 1.3. Викликати метод Print() CA.Print(); // 2. Вивести назви класів-атрибутів, які приєднані до методу Print(). // 2.1. Взяти інформацію про метод Print() класу tCharArray MethodInfo mI = CA.GetType().GetMethod("Print"); // 2.2. Отримати дані про атрибути, які приєднані до методу Print() object[] attrs = Attribute.GetCustomAttributes(mI); // 2.3. Вивести назви атрибутів, які приєднані до методу Print() Console.WriteLine("Attributes that are attached to the Print() method:"); foreach (object at in attrs) { // 2.3.1. Назва атрибуту Console.Write("=> Attribute name: "); Console.WriteLine(at.GetType().Name); // 2.3.2. Номер версії Console.Write("=> Version number: "); PropertyInfo pi = at.GetType().GetProperty("Version"); Console.WriteLine((string)pi.GetValue(at)); } Console.ReadKey(); } } }
Після запуску на виконання програма видає наступний результат
Array CA: a c f = + Attributes that are attached to the Print() method: => Attribute name: VersionAttribute => Version number: 1.0
Пояснимо деякі фрагменти вищенаведеного коду.
Першим створюється клас-атрибут з іменем VersionAttribute з допомогою рядка
... [AttributeUsage(AttributeTargets.Method)] class VersionAttribute : Attribute { ... } ...
Тут AttributeUsage – ім’я вбудованого класу-атрибуту, який обмежує застосування класу VersionAttribute тільки для методів. Це обмеження задається значенням AttributeTargets.Method зі зчислення AttributeTargets. З допомогою зчислення AttributeTargets можна задавати обмеження на створення екземплярів тільки для класів, структур, методів тощо.
Наступним створюється клас CharArray, який містить різні елементи. До методу Print() класу CharArray приєднується атрибут VersionAttribute наступним чином
... [Version(version = "1.0")] // version - іменований параметр public void Print() { Console.WriteLine("Array CA:"); for (int i = 0; i < ca.Length; i++) Console.Write(ca[i] + " "); Console.WriteLine(); } ...
Під час приєднання, заповнюється іменований параметр version значенням “1.0” з допомогою рядка
Version(version = "1.0")
Параметр version є public-членом класу VersionAttribute.
Приєднання класу VersionAttribute до методу Print() може бути в інший спосіб
[Version()] public void Print() { ... }
або
[Version] public void Print() { ... }
У цьому випадку буде викликатись конструктор без параметрів, в якому внутрішнє поле version отримає значення “None”
[AttributeUsage(AttributeTargets.Method)] class VersionAttribute : Attribute { ... public VersionAttribute() { version = "None"; // значення за замовчуванням } ... }
⇑
Зв’язані теми
- Атрибути. Роль атрибутів. Необхідність використання атрибутів. Користувацькі атрибути
- Вбудовані атрибути .NET. Атрибути [AttributeUsage], [Obsolete], [Conditional], [Serializable], [NonSerialized]
⇑