Классы с родовыми параметрами. Универсализм.
Под универсальностью (genericity) понимается способность класса объявлять используемые им типы как параметры. Класс с параметрами, задающими типы, называется универсальным классом (generic class). Пример:
class SomeClass<T> // наш обобщенный класс
{
T first;
T second;
public SomeClass(T input1, T input2)
{
first = input1;
second = input2;
}
}
Ограничение универсальности
Пример:
class Work<T> where T: BaseClass // где Т является наследником от BaseClass
{
public T SomeField;
public Work(T input)
{
SomeField = input;
}
public void ShowData()
{
Console.WriteLine(SomeField.name); //теперь ошибки не будет.
Console.ReadKey();
}
}
С помощью выражения where T : BaseClass мы указываем, что используемый тип T обязательно должен быть классом BaseClass или его наследником.
В определении универсального типа предложение where позволяет задать ограничения для типов, которые можно использовать как аргументы для параметра типа, определенных в универсальном объявлении. Например, можно объявить универсальный класс MyGenericClass, чтобы параметр типа T реализовывал интерфейс IComparable<T>.
Наряду с ограничениями интерфейса предложение where может включать ограничение базового класса, согласно которому тип должен иметь указанный класс как базовый (либо быть таким классом) — только в этом случае его можно будет использовать как аргумент типа для соответствующего универсального типа. Если такое ограничение используется, оно должно быть указано перед любыми другими ограничениями данного параметра типа.
class MyClass<T, U>
where T : class
where U : struct
{ }
Предложение where также может включать ограничение конструктора. С помощью оператора new можно создать экземпляр параметра типа, однако для этого параметр типа должен ограничиваться ограничением конструктора new(). Ограничение new() сообщает компилятору о том, что все предоставленные аргументы типа должны иметь доступный конструктор без параметров (или конструктор по умолчанию). Пример:
public class MyGenericClass<T> where T : IComparable, new()
{
// The following line is not possible without new() constraint:
T item = new T();
}
Ограничение new() отображается в предложении where последним.
Атрибуты
Язык С# позволяет создавать атрибуты для различных элементов языка, таких как типы, методы, поля и свойства классов. Данные, хранимые в атрибутах, можно запрашивать во время выполнения приложения.Использование атрибутов позволяет получать метаданные периода выполнения.Каждый атрибут - это экземпляр класса, производного от System.Attribute.
Класс Attribute пространства имен System предоставляет следующие члены класса:
· GetType - получает объект типа Type текущего экземпляра;
· ToString - возвращает строку, описывающую данный объект;
· IsDefined - определяет, существует ли атрибуты заданного типа, назначенные указываемому члену класса;
· GetCustomAttribute - запрашивает атрибут заданного типа для указанного члена класса.
Для того, чтобы назначить атрибут элементу кода, можно:
1. Определить новый класс атрибута
2. Использовать существующий класс атрибута из библиотеки NET Framework.
Атрибут указывается в квадратных скобках перед элементом, которому он назначается. Назначаемый атрибут инициализируется вызовом конструктора с соответствующими параметрами.
Пример: класс System.ObsoleteAttribute позволяет помечать код и задавать информацию, которая будет отображаться как Warning во время компиляции приложения. Этот класс предназначается для возможности указания некоторого кода модуля как "устаревшего".
// Компилятор будет выдавать предупреждение при
// каждом вызове данного метода, которому назначен
// соответствующий атрибут.
[ObsoleteAttribute ("Сообщение, отображаемое компилятором как Warning")]
public static void M1( ) {return ; }
Также можно создавать собственные атрибуты. Для этого необходимо:
1. Создать новый класс и унаследовать класс System.Attribute
2. Создать поля и конструктор для сохранения различных объектов времени компиляции
Пример:
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : System.Attribute
{
private string name; // Используются как
private int kod; // параметры атрибута
public MyAttribute(string name)
{ /
this.name = name;
this.kod = 12345; }
// Свойство Name
public string Name
{ get { return name; } }
// Свойство Kod
public int Kod
{
get { return kod; }
set {kod=value; }
}
}
}
// Для назначения некоторому классу данного
// атрибута перед объявлением класса следует
// указать строку типа [My("NameClass", Kod=123)]
Теги
XML комментарии, обозначаемые тройным слэшем (///), являются формой XML, используемой для документирования. Теги, используемые в комментариях, полностью настраиваемые; однако есть ряд тегов, которые согласованы с ECMA. Рекомендуемые теги имеют определенные значения для C# компилятора и Visual Studio IDE, но разработчики могут расширять этот рекомендуемый набор, чтобы проводить в жизнь корпоративные стандарты ведения документации, обеспечивать команды разработчиков совместимыми вариантами форматирования или добавлять любую дополнительную, по их мнению, необходимую информацию. Единственным ограничением в XML комментариях является то, что XML, размещенный в тегах, должен быть правильно построен. Это требование выдвигается C# компилятором, как частью процесса компиляции.
C# разработчики могут использовать XML комментарии, чтобы документировать код определенных пользователем типов, включая классы, делегаты, перечисления и структуры. Кроме того, XML комментарии могут быть применены к членам, включая поля, события, свойства, индексаторы и методы. Таким образом создаются стандартизированные комментарии, что облегчает работу разработчика. Кроме того при использовании стандартных тэгов IntelliSense будет автоматически отображать информацию о документированных методах и параметрах, также как и для методов, встроенных во фреймворк.
Список часто используемых тегов:
Тег | Используется для |
<c> | одна строка исходного кода |
<code> | много строк исходного кода |
<example> | пример использования, можно использовать совместно с тегом <code> |
<exception> | позволяет указать, какие исключения наш метод может выдавать |
<include> | позволяет поставить ссылку на файл, в котором содержаться комментарии, используя XPath |
<list> | обычный список |
<para> | а это обычный параграф |
<param> | описания параметра метода, каждый параметр описывается отдельно |
<paramref> | позволяет указать, что слово внутри тега является параметром |
<permission> | позволяет описать права доступа к методу |
<remarks> | дополнительная информация, помимо тега <summary> |
<returns> | описание того, что метод возвращает |
<see> | позволяет указывать ссылки |
<seealso> | текст в секции «Смотри также» |
<summary> | общее описание |
<value> | позволяет описывать свойства |
Корректность класса
Формула корректности программы (участка кода):
{P} A {Q}
Любое выполнение А, начинающееся в состоянии, где P истинно, завершится и в заключительном состоянии будет истинно Q. Формула корректности, называемая также триадой Хоара, - математическое понятие, а не программистская конструкция. Она не является частью языка программирования и введена для того, чтобы выражать свойства программных элементов. В этой формуле А, как было сказано, обозначает операцию, P и Q - свойства вовлекаемых в рассмотрение сущностей, называемые утверждениями (точный смысл этого термина будет определен ниже). Утверждение P называется предусловием, а Q - постусловием.
Класс, подобно всем остальным программным элементам, не может быть корректным или некорректным сам по себе, - только по отношению к некоторой спецификации. Инварианты, предусловия и постусловия, это способ задания спецификации класса. На этой основе можно приступать к определению корректности: класс корректен, если и только если его реализация, заданная подпрограммами, согласована с предусловиями, постусловиями и инвариантами. Предусловие называется условие, которое должно выполняться перед запуском подпрограммы, чтобы обеспечить её корректность (n > 0 для программы вычисления n!). Постусловие - этого условие, которое должно выполняться после работы программы (например a+b=0 для программы a=-b). Инвариантом называется условие, накладывающееся на все экземпляры класса. Например для класса массив верно, что текущий индекс больше нуля и меньше длины массива, в противном случае корректность не гарантируется.
Нотация {P} A {Q} поможет выразить наше определение более строго. Пусть C обозначает класс, Inv - инвариант, r - программа класса. Для каждой программы Bodyr - ее тело, prer(xr), postr(xr) - ее предусловие и постусловие с возможными аргументами xr. Если предусловие или постусловие для программы r опущены, то будем считать их заданными константой True. Наконец, пусть DefaultC обозначает утверждение, выражающее тот факт, что атрибуты класса C имеют значения по умолчанию, определенные их типами.
Тогда истинность класса задается утверждениями:
1. Любая процедура создания (конструктор в случае C#) при ее вызове с выполняемым предусловием должна вырабатывать начальное состояние, удовлетворяющее постусловию и инварианту:
{DefaultC and prep(xp)} Bodyp {postp(xp) and Inv}
2. Любая экспортируемая процедура r (публичный метод), вызываемая в корректном состоянии, удовлетворяющем предусловию и инварианту, должна завершаться в состояниях, удовлетворяющих постусловию и инварианту:
{prer(xr) and Inv} Bodyr {postr(xr) and Inv}
Стиль программирования
Станда́рт оформле́ния ко́да (станда́рт коди́рования, стиль программи́рования) (англ. coding standards, coding convention или programming style) — набор правил и соглашений, используемых при написании исходного кода на некотором языке программирования. Наличие общего стиля программирования облегчает понимание и поддержание исходного кода, написанного более чем одним программистом, а также упрощает взаимодействие нескольких человек при разработке программного обеспечения.
Сюда могут входить требования по именованию членов классов, пространств имён, функциональные имена переменных, размер кода одного метода и т.д.
В качестве примера можно привести некоторые рекомендации к написанию кода на C#
1. Имена классов, интерфейсов, пространств имён и всех публичных членов классов в стиле Pascal casing
2. Имена интерфейсов с префиксом I
3. Локальные переменные, аргументы методов и поля класса в стиле Camel casing
4. Функциональные имена переменных
5. Отсутствие методов с более чем 5 аргументами (в таком случае используются дополнительные типы данных)
6. Небольшие размеры методов
Задачи