3_C#_классы.pptx
- Количество слайдов: 35
Структура программы на С# ü Любая программа на С# - это набор классов, структур и других определенных программистом типов. ü В NET Framework 1. 0 – 1. 1 в одном файле может быть определено несколько типов, но каждый тип должен быть определен в одном файле. ü В NET Framework 2. 0 – 4. 0 можно определить тип (class, struct и interface) в разных файлах. Если тип определяется в разных файлах, перед каждым объявлением типа указывается ключевое слово partial. // файл Abc. cs // файл Abc 1. cs public partial class Abc { private string key; public override string To. String() { return “key=“ + key; } } public partial class Abc { public string Key { get() { return key; } set() { key = value; } } }
Пространства имен ü Позволяют избежать совпадения имен в больших проектах namespace A { class Cls { … } } namespace B { class Cls { … } } A. Cls obj 1 = new A. Cls(); B. Cls obj 2 = new B. Cls(); ü Не связаны с физическим расположением классов A B c 1. dll c 2. dll
Директива using ü Можно использовать сокращенную запись using A; Cls obj = new Cls(); // A. Cls obj = A. Cls(); ü Можно вводить псевдонимы using Btn = System. Windows. Forms. Button; Btn b = new Btn(); // System. Windows. Forms. Button b = // new System. Windows. Forms. Button(); ü Директиву using можно использовать только в начале файла или пространства имен
Вложенные пространства имен ü Пространства имен могут быть вложены namespace A { namespace B { class Cls { … } } } namespace A. B { class Cls { … } } ü using для объемлющего пространства имен не означает сокращение имен для вложенных using A; using A. B; A. B. Cls obj 1; B. Cls obj 2; // Ошибка A. B. Cls obj 1; Cls obj 2; // OK.
Классы и структуры в C# Класс (class) – ссылочный тип. Структура (struct) – тип-значение. Структура не может • иметь деструктор; • иметь конструктор без параметров; • использоваться как базовый тип.
Описание класса в C# модификатор_доступа class имя-класса { … описание данных и методов … } ü Модификаторы доступа для классов: public - класс доступен из других сборок; internal - класс видим только внутри данной сборки. ü Тип доступа по умолчанию – internal.
Члены класса • Константы • Поля (field) • Конструкторы (в том числе без параметров) • Деструктор • Методы (статические и экземплярные) • Свойства (property) • Индексаторы (свойства с параметрами) • События (event) • Вложенные типы
Доступ к членам класса (полям и методам) Модификатор Доступ public в любом методе любой сборки protected в методах самого класса, в методах производных классов любой сборки internal в любом методе данной сборки protected internal в любом методе данной сборки, в производных классах других сборок private только в методах самого класса ü Вложенный тип может иметь любой из 5 модификаторов доступа, но уровень доступа не может быть выше уровня доступа объемлющего типа.
Константы ü Локальные переменные, поля классов и структур могут иметь модификатор const. ü Константы инициализируются при объявлении. ü Константы применяются для улучшения читаемости кода. public class Text. Editor { const int Max. Lines = 10000; … }
Поля readonly ü Поля классов и структур могут иметь модификатор readonly. Поле с модификатором readonly инициализируется в конструкторе и больше не изменяется. ü readonly поля могут инициализироваться явно public class Text. Editor { readonly int Max. Lines = 10000; … } ü readonly поля могут инициализироваться по умолчанию public class Text. Editor { readonly int Max. Lines; // Значение 0 … } ü readonly поля могут инициализироваться в конструкторе public class Text. Editor { readonly int Max. Lines; public Text. Editor(int max. Lines) { Max. Lines = max. Lines; string[] lines = new string[Max. Lines]; } … }
Статические методы и данные ü Методы и поля данных объявляются как статические с помощью модификатора static. ü Статические данные совместно используются всеми экземплярами класса. ü Статический метод вызывается через класс. ü Статический метод может быть вызван еще до создания экземпляра (instance) класса. double res = Math. Sin(Math. PI/6); Console. Write. Line("res= {0}", res);
Инициализация объектов: конструкторы ü При создании объекта выполняются два действия: • выделяется память под объект; • выделенный участок памяти инициализируется. T t = new T(); Выделение памяти Инициализация объекта ü Инициализацию выделенного участка памяти выполняет специальный метод класса – конструктор. ü В классе может быть определено несколько конструкторов с различными списками параметров.
Конструктор по умолчанию ü Конструктор без параметров называется конструктором по умолчанию. ü Если конструктор по умолчанию не задан, компилятор сам создает конструктор по умолчанию. ü Если в классе определен хотя бы один конструктор, конструктор по умолчанию не создается. Когда нужен свой конструктор по умолчанию? • при инициализации объекта требуется выполнить действия более сложные, чем простое присваивание значений полям данных; • требуется изменить уровень доступа к конструктору.
Singleton pattern ü Иногда требуется ограничить количество экземпляров объекта одним. public class Singleton { private Singleton() { // выполняем инициализацию } public static Singleton Get. Instance() { if( instance == null) instance = new Singleton(); return instance; } private static Singleton instance; } // Singleton s = new Singleton(); // Ошибка! Singleton s 2 = Singleton. Get. Instance(); // OK.
Списки инициализаторов ü Чтобы избежать дублирования кода, можно вызывать один конструктор из другого при помощи специальной синтаксической конструкции - списка инициализаторов. public T(string key) { this. key = key; nobj++; } public T(string key, double []dt) : this(key) { this. dt = new double[dt. Length] ; Array. Copy(dt, this. dt, dt. Length); } ü Список инициализаторов применим только в конструкторах и не может ссылаться сам на себя (рекурсивный вызов конструкторов).
Статический конструктор ü При выполнении программы под управлением CLR классы могут загружаться во время выполнения программы. ü После загрузки класса, но перед его использованием всегда выполняется статический конструктор (конструктор класса), инициализирующий статические поля данных. public class T { static int st; static T() { // Инициализация статических полей } … }
Свойства статического конструктора ü Для статического конструктора гарантируется, что: • Статический конструктор будет вызван перед созданием первого экземпляра объекта. • Статический конструктор будет вызван перед обращением к любому статическому полю класса или статическому методу класса. • Статический конструктор будет вызван не более одного раза за все время выполнения программы. ü Статический конструктор не может иметь модификатор доступа. ü Статический конструктор не может принимать параметры. ü Статический конструктор не может быть вызван явно.
Статические классы ü Статические классы поддерживаются версиями 2. 0 и выше. ü Класс может содержать только статические члены с типом доступа private, internal или public. ü Нельзя создать экземпляр статического класса. ü Любой статический класс имеет непосредственный базовый класс object и не может быть базовым для другого класса. ü В классе нельзя определить операции. ü Примеры статических классов – System. Console и System. Math.
Свойства ü Свойство - пара методов со специальными именами: • метод get() вызывается при получении значения свойства; • метод set() вызывается при задании значения свойства. ü Каждое свойство имеет имя и тип. ü Если определен только метод get , свойство доступно только для чтения. Если определен только метод set, свойство доступно только для “записи”. ü Свойство может быть связано с закрытым полем класса. ü Свойства работают немного медленнее прямого доступа к полю.
Cвойства. Пример partial class T { private string key; private double [] dt = new double[0]; public string Key { get { return key; } set { key = value; } } public double Sum { get { double s = 0; for ( int j=0; j < dt. Length; j++ ) s += dt[j]; return s; } } } T t = new T(); t. Key = “abcd”; double s = t. Sum;
Автореализуемые свойства ü Для автореализуемых свойств компилятор генерирует закрытые поля для хранения значения и методы для доступа к ним. ü В примере свойства Key и Name объявлены как автореализуемые: class T { private string test; public string Key { get; private set; } public string Name { get; set; } public T() { Key = "Key_1“; } public T(string key) { Key = key; } public string Test { get { return test; } set { test = value; } } public override string To. String() { return Key + " " + Name + " } " + test; }
Инициализаторы объектов ü Свойствам, в том числе автореализуемым, можно присвоить значения в инициализаторах объектов. static void Main(string[] args) { T t 1 = new T(); t 1. Name = "Abc"; // t 1. Key = "Key 1"; Console. Write. Line(t 1); T t 2 = new T() { Name = "Efg" }; Console. Write. Line(t 2); T t 3 = new T("Key_2") { Name = “Hjk" , Test = "Xyz" }; Console. Write. Line(t 3); // // } Вывод Key_1 Abc Key_1 Efg Key_2 Hjk Xyz
Зачем нужны свойства? • Свойства реализуют принцип инкапсуляции. • Объявление автореализуемых свойств дает возможность в следующих версиях типа изменить их реализацию, при этом открытый интерфейс типа останется без изменений. • Свойства активно используются визуальными инструментами разработки.
Индексаторы (свойства с параметрами) ü В С# для определения оператора индексирования [ ] используется специальный синтаксис. ü Индексатор имеет определенный тип. ü Индексатор может быть перегружен – можно определить индексаторы с различным числом и типами параметров. partial class T { private double[] dt = new double[0]; public double this[int j] { get { return dt[j]; } set { dt[j] = value; } } public double this[int i, int j] { get { return dt[Math. Abs(i - j)]; } set { dt[Math. Abs(i - j)] = value; } } }
Индексаторы. Пример partial class T { private string key; private double[] dt = new double[0 ]; public T( string key, double[] dt) { this. key = key; this. dt = dt; } public double this[int j] { get { return dt[j]; } set { dt[j] = value; } } public double this[int i, int j] { get { return dt[Math. Abs(i - j)]; } set { dt[Math. Abs(i - j)] = value; } } public override string To. String() { string str = key; for (int j = 0; j < dt. Length; j++) str += " return str; } } class Program { static void Main(string[] args) { double[] data = new double[] { 1, 2, 3 }; T test = new T("abc", data); test[1] = 7; Console. Write. Line(test); test[3, 1] = 10; Console. Write. Line(test); } // abc 1 7 3 // abc 1 7 10 " + dt[j];
Передача параметров размерных типов struct S {…}; class Class 1 { void F (S p 1, ref S p 2, out S p 3) {. . . } p 1 передается по значению - делается ограниченная (shallow) копия, изменения не возвращаются в контекст вызова; p 2 и p 3 передаются по ссылке, изменения возвращаются в контекст вызова; p 2 перед вызовом должен быть инициализирован; p 3 может быть не инициализирован, в методе должно быть присвоено значение.
Передача параметров ссылочных типов class Class 1 { public static void f 1 (double[] darr) // аналог в C++ double* { darr[0] = 555; double[] df = new double[]{5, 15}; darr = df; } public static void f 2 (ref double[] darr) // аналог в C++ double* & { darr[0] = 777; double[] df = new double[]{7, 17}; darr = df; } static void Main(string[] args) { double[]dm = {1, 2, 3}; Class 1. f 1(dm); foreach(double d in dm) Console. Write(" {0}", d); // 555 2 3 Class 1. f 2(ref dm); foreach(double d in dm) Console. Write(" {0}", d); // 7 17 } } f 1 делается копия ссылки dm, при выходе из f 1 значение dm не изменится, но элементы массива изменятся f 2 при выходе из f 2 значение dm изменится
Методы с переменным числом параметров class Class 1 {. . . public static void f (T 1 p 1, T 2 p 2, params T[] p) {. . . } } ü Только последний параметр метода может иметь модификатор params. ü Параметр c модификатором params должен быть одномерным массивом (любого типа). ü Если при вызове не найден метод с точным совпадением типов фактических параметров, параметры собираются в массив и вызывается метод с модификатором params.
Именованные и опциональные параметры ü Именованные и опциональные параметры поддерживаются версией языка C# Visual C# 2010. Могут использоваться в методах, индексаторах, конструкторах и делегатах. ü Именованные параметры позволяют при вызове метода указать значение для формального параметра с использованием его имени ( с изменением порядка параметров в списке формальных параметров). ü Опциональные параметры дают возможность при вызове метода опустить значения тех параметров, для которых при определении метода были указаны значения по умолчанию. ü При использовании именованных и опциональных параметров они вычисляются в том порядке, в котором они были перечислены в списке формальных параметров.
Именованные и опциональные параметры. Пример class Program { static void Main(string[] args) { string res_1 = Abc. F(count: 5); Console. Write. Line(res_1); class Abc { string str_1; string str_2; string str_3; public Abc() { str_1 = "Default_1"; str_2 = "Default_2"; str_3 = "Default_3"; } Abc abc_1 = new Abc(); Console. Write. Line(abc_1); Abc abc_2 = new Abc("test_1"); Console. Write. Line(abc_2); public Abc( string str_1 = "s 1", string str_2 = "s 2", string str_3 = "s 3") { this. str_1 = str_1; this. str_2 = str_2; this. str_3 = str_3; } Abc abc_3 = new Abc(str_2: "test_2"); Console. Write. Line(abc_3); } } public static string F( char symbol = '1', int count = 2) { return new string(symbol, count ); } public override string To. String() { return str_1 + " " + str_2 + " } Вывод: 11111 Default_1 test_1 " + str_3 ; } s 1 Default_2 s 3 test_2 s 3 Default_3
Перегрузка операторов для классов и структур ü Возможна перегрузка бинарных операторов: + - * % / | & ^ << >> и унарных операторов + - ~ ! ++ -ü Операторы перегружаются с помощью открытых статических методов. Один из параметров должен совпадать с объемлющим типом. ü Операторы (составные операции присваивания) += -= *= /= %= >>= <<= &= |= ^= не могут быть перегружены, но когда перегружены соответствующие им бинарные операции, последние используются при вычислении выражений с операторами += -= *= /= %= >>= <<= &= |= ^=
Перегрузка операторов. Пример struct Rational { int a, b; public Rational(int aa, int bb) { a = aa; b = bb; } // бинарный + public static Rational operator+ (Rational ls, Rational rs) { return new Rational (ls. a*rs. b + ls. b*rs. a , ls. b*rs. b); } // унарный public static Rational operator- (Rational r) { return new Rational(-r. a, r. b); } } static void Main(string[] args) { Rational r 1 = new Rational(1, 2); Rational r 2 = new Rational(1, 3); Rational r 3 = r 1 + r 2; r 2 += r 1; // Вычисляется как r 2 = r 2 + r 1; Rational r 4 = -r 1; }
Перегрузка операторов ++ и -Операторы ++ и -- могут применяться в префиксной и постфиксной форме: r 1 = ++a 1; // Вычисляется как a 1 = operator ++ (a 1); r 1 = a 1; r 2 = a 2++; // Вычисляется как r 2 = a 2; a 2 = operator ++ (a 2); struct Rational { int a, b; public Rational(int aa, int bb) { a = aa; b = bb; } public static Rational operator ++ (Rational r) { return new Rational(r. a + r. b, r. b); } public override string To. String() { return "(" + a + ", " + b +")"; } } static void Main(string[] args) { Rational a 1 = new Rational(1, 2); Rational a 2 = new Rational(1, 2); Rational r 1 = ++a 1; Console. Write. Line("a 1={0} r 1={1}", a 1, r 1); // a 1=(3, 2) r 1=(3, 2) Rational r 2 = a 2++; Console. Write. Line("a 2={0} r 2={1}", a 2, r 2); // a 2=(3, 2) r 2=(1, 2) }
Перегрузка операторов сравнения ü Перегрузка операторов сравнения возможна только в парах: <= и >= <и> == и != ü Чтобы избежать дублирования кода и связанных с ним ошибок, рекомендуется поместить код для сравнения в единственный статический метод и вызывать его из методов, перегружающих операторы. struct Rational { int a, b; … public static int Compare(Rational ls, Rational rs) { long d = ls. a*(long)rs. b - rs. a*(long)ls. b; if(d < 0) return -1; // Первый аргумент меньше второго else if(d > 0) return 1; // Первый аргумент больше второго else return 0; } public static bool operator < (Rational ls, Rational rs) { return (Compare(ls, rs) < 0); } public static bool operator > (Rational ls, Rational rs) { return (Compare(ls, rs) > 0); } }
Операторы преобразования типов ü Можно объявлять явные и неявные преобразования для структуры или класса как статические методы. struct Rational{ Оператор явного преобразования boolint a, b; >Rational public static explicit operator Rational ( bool val ) { int a = val ? 1 : -1; return new Rational( a, 1); } Оператор неявного преобразования intpublic static implicit operator Rational ( int val ) >Rational { return new Rational(val, 1); } public static explicit operator bool ( Rational r ) { return r. a*r. b > 0; } … } static void Main(string[] args) { Rational r 1 = 5; bool b 1 = true; Rational r 2 = (Rational) b 1; bool b 2 = ( bool) r 1; }
3_C#_классы.pptx