Читайте также:
|
|
При розробці узагальнень слід пам’ятати, що обмеження операцій в існуючих версіях мови C# не підтримуються. Це означає, що наступне обмеження є помилкою.
// Недопустиме узагальнення! public class BasicOperations<T> where T: operator +, operator -, operator *, operator / { public T Add(T argl, T arg2) { return argl + arg2; } public T Sub(T argl, T arg2) { return argl - arg2; } public T Mul(T argl, T arg2) { return argl * arg2; } public T Div(T argl, T arg2) { return argl / arg2; } } |
Необхідність такого роду обмежень (коли в узагальненні виникає потреба у використанні операцій додавання, множення, віднімання, ділення, тощо для об’єктів-змінних параметру типу) на практиці виникає досить часто. Наприклад, така необхідність буде виникати при реалізації багатьох методів узагальнення Matrix<T> − узагальненого класу для роботи з матрицями, елементи яких мають тип T. Зокрема, операцій додавання, множення та визначення протилежного над об’єктами-змінними типу T буде вимагати метод обчислення визначника квадратної матриці з елементами загального типу T.
Та все ж таки бажаного результату у цьому випадку можна досягти. Наприклад, для цього ми можемо використати інтерфейс, який підтримує зазначені операції, і потім вказати обмеження на цей інтерфейс. Реалізуємо цей підхід на прикладі розробки узагальнення Matrix<T> для роботи з матрицями.
Нагадаємо, що матриця − математичний об’єкт, записаний у вигляді прямокутної таблиці елементів кільця або поля (наприклад раціональних, p-адичних, дійсних або комплексних чисел). В свою чергу поле − це всяка множина з двома бінарними операціями (адитивною та мультиплікативною), якщо вона разом з цими операціями утворює комутативне асоціативне кільце з одиницею та всі ненульові елементи якої мають обернений. Програмно таку абстракцію можна подати у вигляді наступного узагальненого інтерфейсу:
interface IField<F> { // адитивна операція (додавання) поля F F Add(F a, F b); // мультиплікативна операція (множення) поля F F Mul(F a, F b); // властивість, яка всякому елементу поля F повертає його протилежний F Adverse { get; } // властивість, яка всякому елементу поля F повертає його обернений F Inverse { get; } // властивість, яка повертає нуль поля F F Zero { get; } // властивість, яка повертає одиницю поля F F One { get; } } |
Тепер, як було зазначено вище, визначимо узагальнений клас Matrix<T>, наклавши на параметр типу T обмеження на реалізацію узагальненого інтерфейсу IField<T>:
class Matrix<T> where T: IField<T> { // прямокутний масив елементів матриці T[,] array; } |
Використання операцій над екземплярами параметру типу T проілюструємо на прикладі реалізації методу Determinant для обчислення визначника матриці.
Нагадаємо, що мінором елемента (ij-мінором) матриці , називається матриця розмірності , яка отримується із матриці видаленням i-го рядка та
j-го стовпця. Визначник або детермінант матриці розмірності можна обчислити рекурсивно за допомогою мінорів наступним чином:
Таким чином отримуємо:
class Matrix<T> where T: struct, IField<T> { // прямокутний масив елементів матриці T[,] array; // конструктори з параметрами public Matrix(int rowCount, int colCount) { array = new T[rowCount, colCount]; } public Matrix(T[,] arg) { array = arg; } // властивість повернення кількості рядків матриці public int RowCount { get { return array.GetLength(0); } } // властивість повернення кількості стовпців матриці public int ColCount { get { return array.GetLength(1); } } // індексатор для зручності доступу до елементів матриці public T this[int i, int j] { get { return array[i,j]; } } // метод визначення ij-мінору матриці public Matrix<T> IJMinor(int I, int J) { Matrix<T> tmp = new Matrix<T>(RowCount-1, ColCount-1); for (int i = 0; i < RowCount; i++) for (int j = 0; j < ColCount; j++) { if (i < I && j < J) tmp.array[i, j] = array[i, j]; if(i < I && j > J) tmp.array[i, j - 1] = array[i, j]; if (i > I && j < J) tmp.array[i - 1, j] = array[i, j]; if (i > I && j > J) tmp.array[i - 1, j - 1] = array[i, j]; } return tmp; } // властивість, що повертає значення детермінанту квадратної матриці public T Determinant { get { if (RowCount == ColCount) { if (RowCount == 1) return array[0, 0]; else { // присвоєння змінній-об’єкту типу T значення за замовчуванням[11] T det = default(T); for (int j = 0; j < RowCount; j++) if (j % 2 == 0) det = det.Add(det, det.Mul(array[0, j], IJMinor(0, j).Determinant)); else det = det.Add(det, det.Mul(array[0, j], IJMinor(0, j).Determinant).Adverse); return det; } } else throw new Exception("Детермiнант не є числовою характеристикою прямокутної матрицi!"); } } // метод виводу матриці на екран public void Display() { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColCount; j++) Console.Write("{0} ", array[i,j]); Console.WriteLine(); } } } |
Із визначення класу Matrix<T> випливає, що в якості аргументу типу T можуть виступати тільки ті класи, які реалізують узагальнений інтерфейс IField<T>. Реалізацію IField<T> проілюструємо на прикладі визначення класу для поля дійсних чисел у вигляді наступної структури:
struct Real: IField<Real> { // значення елемента поля дійсних чисел double value; // нуль поля дійсних чисел static double zero = 0; // одиниця поля дійсних чисел static double one = 1; // реалізація узагальненого інтерфейсу IFieldStructure для поля дійсних чисел public Real Add(Real a, Real b) { return a.value + b.value; } public Real Mul(Real a, Real b) { return a.value * b.value; } public Real Adverse { get { return -value; } } public Real Inverse { get { return 1 / value; } } public Real Zero { get { return Real.zero; } } public Real One { get { return Real.one; } } // перевантаження неявного приведення типу double до Real public static implicit operator Real(double v) { Real tmp = new Real(); tmp.value = v; return tmp; } // перевантаження неявного приведення типу Real до double public static implicit operator double(Real v) { return v.value; } // перевантаження методу базового класу public override string ToString() { return value.ToString(); } } |
Тепер розроблене нами узагальнення Matrix<T> може бути використане з аргументами типу Real:
Matrix<Real> M = new Matrix<Real>(new Real[,]{{1,2,3},{2,1,3},{3,1,2}}); Console.WriteLine(M.Determinant); |
[1] Фреймворк − програмне забезпечення, що полегшує розробку та об’єднання різних компонентів великого програмного проекту. На відміну від бібліотек, які поєднують набір підпрограм близьких за функціональністю, фреймворк містить у собі велику кількість різних за призначенням бібліотек, а також допоміжні програми, мову сценаріїв та інше програмне забезпечення.
[2] Компіляція − механізм чи процедура перетворення програмного коду, написаного на одній мові програмування у еквівалентний байт-код (псевдокод) чи у машинний код.
Псевдокод − машинно-незалежний код низького рівня, що генерується транслятором і виконується інтерпретатором.
Машинний код − набір команд, що може зрозуміти й обробити центральний процесор комп’ютера без транслятора.
Транслятор − програма або технічний засіб, що виконує перетворення чи іншу обробку текстів програм.
Інтерпретатор − програма чи технічні засоби, необхідні для виконання інших програм. Це різновид транслятора, який здійснює покомандну обробку, перетворення у машинний код та виконання програми (на відміну від компілятора, який транслює у машинні коди всю програму без її виконання). Інтерпретатори можуть працювати як з вихідним кодом програми (англ. source code), написаним мовою програмування, так і з байт-кодом (інтерпретатори байт-коду).
[3] Псевдокод MSIL визначає набір переносних інструкцій, що не залежать від типу (архітектури) процесора. Це платформо незалежний код.
[4] Хоча в більшості випадків методи Main() повертають значення типу void, можливість повертати значення типу int із Main() дозволяє погоджувати C# з іншими мовами, що базуються на C. За домовленістю, повернення значення 0 свідчить про те, що виконання програми пройшло успішно, а будь-якого іншого значення (наприклад, -1) − що в ході виконання програми відбулася помилка (слід мати на увазі, що значення 0 повертається автоматично навіть у випадку, якщо метод Main() повертає void).
[5] У якості альтернативи, замість стандартного циклу for для проходу по вхідному масиву рядків можна також використовувати ключове слово foreach (будемо розглядати пізніше).
[6] Інтегроване середовище розробки − система програмних засобів для розробки програмного забезпечення. Як правило середовище розробки містить у собі текстовий редактор, компілятор і/або інтерпретатор, засоби автоматизації
[7] Програма установки.NET Framework 4.0 SDK (dotnetfx40_full_setup. exe) доступна на сторінці завантаження.NET за адресою http://msdn.microsoft.com/netframework.
[8] Загальноприйнятою є домовленість надавати файлу з кодом на С# розширення.cs.
[9] Про інші параметри компілятора C# для роботи із вихідними файлами, а також параметри для роботи зі вхідними файлами, ресурсами, помилками, тощо можна дізнатись увівши в командному рядку команду сsc -? або csc /help.
[10] У мові C # є два різновиди типів даних: типи значень (value based types) та посилальні типи (reference based types). Змінна посилального типу зберігає тільки адреси об’єктів, а змінна типу значення − сам об’єкт. До типів значень належать структури (структурами у мові C# є і всі прості типи даних) та переліки. Всі інші типи у мові C# є посилальними, зокрема тип string.
[11] Ключове слово default в узагальненнях використовується для встановлення значень за замовчуванням для параметра типу. Замовчування для параметрів типу наступні:
· значення за замовчуванням числових величин дорівнює нулю;
· посилальні типи мають значення за замовчуванням null;
· поля структур встановлюються у 0 (для типів-значень) або в null (для посилальних типів).
Дата добавления: 2015-10-26; просмотров: 124 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Абстрактні класи | | | Деякі класи неузагальнених колекцій |