Вбудовані атрибути .NET. Атрибути [AttributeUsage], [Obsolete], [Conditional], [Serializable], [NonSerialized]
Перед вивченням даної теми рекомендується ознайомитись з темою:
Зміст
- 1. Вбудовані атрибути .NET. Огляд. Перелік
- 2. Атрибут [AttributeUsage]. Зчислення AttributeTargets
- 3. Приклад застосування атрибуту AttributeUsage
- 4. Атрибут [Obsolete]. Приклад
- 5. Застосування атрибуту [Conditional]. Приклад
- 6. Застосування атрибутів [Serializable] та [NonSerialized]. Серіалізація. Приклад
- Зв’язані теми
Пошук на інших ресурсах:
1. Вбудовані атрибути .NET. Огляд. Перелік
Мова C# передбачає використання вбудованих (стандартних) атрибутів (класів-атрибутів), які можна приєднувати до власно-розроблених елементів програми (класів, методів, структур тощо). Нижче наведено найбільш поширені з них:
- AttributeUsage;
- Obsolete;
- Conditional;
- Serialize;
- NonSerialized;
- інші атрибути.
⇑
2. Атрибут [AttributeUsage]. Зчислення AttributeTargets
Атрибут AttributeUsage дозволяє визначити типи елементів, до яких може бути застосований оголошуваний користувацький атрибут. Це означає, що якщо оголошується користувацький клас-атрибут, то існує можливість вказати застосування цього класу строго для визначених елементів програми, наприклад методів, полів, властивостей тощо. Перелік елементів визначається у зчисленні AttributeTargets.
Атрибут AttributeUsage може бути використаний тільки для класів-атрибутів, тобто для класів, які успадковані від класу System.Attribute. Якщо спробувати використати атрибут AttributeUsage для будь-якого класу, що не є атрибутом (не успадкований від System.Attribute), то виникне помилка на етапі компіляції.
У найбільш загальному випадку використання атрибуту AttributeUsage має такий вигляд:
[AttributeUsage(AttributeTargets validElements)] class MyClassAttribute : Attribute { ... }
тут validElements – перелік значень зі зчислення AttributeTargets, які визначають елементи, що можуть приєднувати користувацький клас-атрибут MyClassAttribute. Перелік значень формується через оператор | (АБО).
Зчислення AttributeTargets визначає наступні значення:
- All або AttributeTargets.All – означає, що користувацький клас-атрибут може бути приєднаний до будь-якого елементу програми;
- Assembly – оголошуваний (користувацький) атрибут може бути застосований до збірки;
- Class – атрибут застосовується до класу;
- Constructor – оголошуваний атрибут може бути застосований до конструктора;
- Delegate – атрибут може бути застосований до делегату;
- Enum – атрибут може бути застосований до зчислення;
- Event – атрибут може бути застосований до події;
- Field – атрибут може бути застосований до поля;
- GenericParameter – атрибут може бути застосований до узагальненого параметру;
- Interface – атрибут може бути застосований до інтерфейсу;
- Method – атрибут може бути застосований до методу;
- Module – застосовується до модуля;
- Parameter – застосовується до параметру;
- Property – застосовується до властивості;
- ReturnValue – застосовується до значення що повертається;
- Struct – застосовується до структури.
⇑
3. Приклад застосування атрибуту AttributeUsage
Нехай потрібно оголосити користувацький атрибут (клас-атрибут), який задає коментар до елементу. Потрібно забезпечити використання цього атрибуту строго для класів, структур та методів. У цьому випадку оголошення класу-атрибуту та його приєднання до іншого класу може бути, наприклад, таким
... // Оголошення атрибуту (класу-атрибуту), що визначає коментар. // Клас може застосовуватись строго до класів, структур та методів. [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method )] class CommentAttribute : Attribute { // внутрішнє поле-коментар, позиційний параметр у конструкторі private string comment; // Конструктор класу public CommentAttribute(string _comment) { comment = _comment; } // Властивість, що повертає коментар public string Comment { get { return comment; } } } // Приєднання атрибуту CommentAttribute до класу MyClass [Comment("This is a class")] class MyClass { // ... // Приєднання атрибуту CommentAttribute до методу [Comment("This is a method")] public void Print() { Console.WriteLine("Class MyClass"); } } // Приєднання атрибуту CommentAttribute до структури [Comment("This is a structure")] struct XYZ { public double x; public double y; public double z; } ...
Якщо спробувати приєднати атрибут до іншого елементу крім класу, структури, методу то компілятор видасть помилку. Тобто, якщо спробувати приєднати атрибут CommentAttribute до зчислення
... [Comment("This is a enumeration")] enum Colors { Red = 1, Blue = 2, Yellow = 4 } ...
то програма не відкомпілюється.
⇑
4. Атрибут [Obsolete]. Приклад
З допомогою атрибуту Obsolete можна помітити елемент програми як застарілий. Загальна форма використання (приєднання) атрибуту наступна:
[Obsolete(message)]
тут message – деяке повідомлення, що видається компілятором під час компіляції елементу програми, до якого приєднується (застосовується) атрибут Obsolete.
Приклад. У прикладі оголошується клас Complex, що реалізує комплексне число. У класі оголошуються два перевантажені методи Print(), які мають наступну сигнатуру:
void Print(); void Print(string msg);
Перший метод Print() без параметрів помічений як застарілий з допомогою атрибуту Obsolete
... [Obsolete("Old version of Print() method.")] public void Print() { Console.WriteLine("re = {0:f2}, im = {1:f2}", re, im); } ...
У функції main() в рядку
cm.Print();
компілятор видає попередження, що застосовується застаріла версія методу Print().
Нижче наведено весь код демонстраційної програми типу Console Application.
using System; namespace ConsoleApp19 { // Клас, що описує комплексне число class Complex { // Внутрішні поля - дійсна та уявна частини числа private double re, im; // Конструктор public Complex(double _re, double _im) { re = _re; im = _im; } // Властивості доступу public double Re { get { return re; } set { re = value; } } public double Im { get { return im; } set { im = value; } } // Метод, що виводить комплексне число - застарілий варіант [Obsolete("Old version of Print() method.")] public void Print() { Console.WriteLine("re = {0:f2}, im = {1:f2}", re, im); } // Метод, що виводить комплексне число - нова версія public void Print(string msg) { Console.Write(msg + ": "); Console.WriteLine("re = {0:f2}, im = {1:f2}", re, im); } } class Program { static void Main(string[] args) { Complex cm = new Complex(2.5, 1.8); // 1. Виклик старої версії методу Print() // тут компілятор видає попередження (warning) // що використовується застаріла версія методу Print() Console.Write("cm: "); cm.Print(); // 2. Виклик нової версії методу Print() cm.Print("cm"); Console.ReadKey(); } } }
Результат виконання програми
cm: re = 2.50, im = 1.80 cm: re = 2.50, im = 1.80
⇑
5. Застосування атрибуту [Conditional]. Приклад
Атрибут [Conditional] забезпечує виклик методів на основі ідентифікатора, який визначений в директиві #define.
Іншими словами, якщо в коді програми визначено деякий ідентифікатор з допомогою директиви #define
#define Identifier
то в екземплярі класу буде викликатись метод, що помічений цим ідентифікатором
class SomeClass { ... // Приєднання атрибуту [Conditional] до методу [Conditional("Identifier") return_type MethodName(parameters) { // Цей метод буде викликатись // ... } }
тут
- return_type – тип, який повертає метод з іменем MethodName();
- parameters – параметри методу MethodName().
Всі інші методи, що помічені іншим ідентифікатором, не будуть виконуватись. Також будуть виконуватись ті методи, до яких атрибут [Conditional] не приєднано.
Щоб застосувати атрибут [Conditional], у програмі потрібно підключити простір імен System.Diagnostics
using System.Diagnostics;
Приклад. У прикладі демонструється застосування атрибуту [Conditional] з різними ідентифікаторами до різних методів класу TestMethods.
// Визначити ідентифікатор DoMethod2 #define DoMethod2 using System; using System.Diagnostics; namespace ConsoleApp19 { // Клас, що містить методи, до яких прикріплено атрибут [Conditional] class TestMethods { // До методу Method1() приєднано атрибут [Conditional] [Conditional("DoMethod1")] public void Method1() { Console.WriteLine("Method1()"); } // До методу Method2() приєднано атрибут [Conditional] [Conditional("DoMethod2")] public void Method2() { Console.WriteLine("Method2()"); } // До методу Method3() приєднано атрибут [Conditional] [Conditional("DoMethod3")] public void Method3() { Console.WriteLine("Method3()"); } // До методу Method4() не приєднано ніяких атрибутів public void Method4() { Console.WriteLine("Method4()"); } } class Program { static void Main(string[] args) { // Демонстрація роботи атрибуту [Conditional] // Створити екземпляр класу TestMethods TestMethods tm = new TestMethods(); // Викликати послідовно 4 методи tm.Method1(); // метод не виконається tm.Method2(); // метод виконається tm.Method3(); // метод не виконається tm.Method4(); // метод виконається Console.ReadKey(); } } }
Результат виконання програми
Method2() Method4()
Як видно з результату, викликаються наступні два методи з чотирьох:
- Method2() – до цього методу приєднано атрибут [Conditional] з ідентифікатором “Method2”, який визначений у директиві #define на початку програми;
- Method4() – метод, до якого ніяких атрибутів не приєднано.
⇑
6. Застосування атрибутів [Serializable] та [NonSerialized]. Серіалізація. Приклад
Якщо потрібно, щоб клас чи структура підтримували серіалізацію потрібно перед їх оголошенням вказати атрибут [Serializable]. Підтримка серіалізації для класу чи структури означає, що цей клас (структура) має можливість зберігати свій стан у байтовому потоці. При збереженні стану, фактично зберігаються дані класу, якими є внутрішні поля класу.
Загальна форма оголошення класу, що підтримує серіалізацію наступна:
[Serializable] class SomeClass { ... }
Бувають випадки, коли з набору внутрішніх полів класу, не обов’язково зберігати їх усіх. Для того, щоб вказати поля класу, які не потрібно серіалізувати, використовується атрибут [NonSerialized]. Атрибут [NonSerialized] застосовується тільки до полів класу. У найбільш простому випадку застосування атрибуту [NonSerialized] до поля має вигляд
[Serializable] class SomeClass { ... [NonSerialized] type fieldName; ... }
тут
- fieldName – ім’я поля в класі SomeClass;
- type – тип поля fieldName.
Приклад. Задано структуру Book, що описує книгу в бібліотеці. У структурі оголошуються наступні загальнодоступні поля:
- поле title – назва книги;
- поле author – ім’я автора книги;
- year – рік видання книги;
- number – порядковий номер книги в переліку книг.
Структура Book підтримує серіалізацію у бінарному форматі. Для підтримки серіалізації до структури приєднується атрибут [Serializable]. Збереження структури у байтовому потоці здійснюється з допомогою класу BinaryFormatter. Клас BinaryFormatter реалізує інтерфейс IFormatter. У цьому інтерфейсі визначені два методи, які застосовуються до екземпляру структури:
- Serialize() – здійснює збереження структури у байтовому потоці;
- Deserialize() – здійснює відновлення структури Book з байтового потоку.
У програмі відбувається демонстрація серіалізації екземпляру структури типу Book. Поле number не зберігається у байтовому потоці (не серіалізується).
Код демонстраційної програми наступний.
using System; using System.IO; // потрібно для операцій вводу/виводу // Необхідно для використання класу BinaryFormatter using System.Runtime.Serialization.Formatters.Binary; namespace ConsoleApp19 { // Структура, що описує книгу. // Структура підтримує серіалізацію. [Serializable] struct Book { // Ці поля будуть серіалізовані (збережені) public string title; public string author; public int year; [NonSerialized] public int number; // це поле не серіалізується // Конструктор public Book(string title, string author, int year, int number) { this.title = title; this.author = author; this.year = year; this.number = number; } // Метод, що виводить інформацію про книгу public void Print(string msg) { Console.WriteLine(msg); Console.WriteLine("Author: " + author); Console.WriteLine("Title: " + title); Console.WriteLine("Year: " + year); } } class Program { static void Main(string[] args) { // 1. Створити екземпляр типу Book Book b1 = new Book("The C Programming Language", "D. Ritchie", 1978, 1); // 2. Створити екземпляр типу BinaryFormatter BinaryFormatter binaryFormat = new BinaryFormatter(); try { // 3. Зберегти у файлі books.bin дані екземпляру структури b1 using (Stream fOut = File.Create("books.bin")) { binaryFormat.Serialize(fOut, b1); } Console.WriteLine("Binary serialize is done."); // 4. Зчитати дані з файлу books.bin в іншу структуру b2 // і вивести прочитану інформацію на екран. using (Stream fIn = File.OpenRead("books.bin")) { Book b2; b2 = (Book)binaryFormat.Deserialize(fIn); b2.Print("The instance of b2:"); Console.WriteLine("Binary deserialize is done."); } } catch (Exception e) { Console.WriteLine(e.Message); } Console.ReadKey(); } } }
Результат виконання програми
Binary serialize is done. The instance of b2: Author: D. Ritchie Title: The C Programming Language Year: 1978 Binary deserialize is done.
⇑
Зв’язані теми
- Атрибути. Роль атрибутів. Необхідність використання атрибутів. Користувацькі атрибути
- Конструктори в класах атрибутів. Позиційні та іменовані параметри. Приклади
⇑