Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Перегрузка операций



Язык C# позволяет организовать для объектов пользовательского класса или структуры перегрузку операций. Могут быть перегружены унарные операции +, -,!, ~, ++, --, true, false и бинарные операции +, -, *, /, %, &, |, ^, <<, >>, ==,!=, >, <, >=, <=. При перегрузке бинарной операции автоматически перегружается соответствующая операция с присваиванием (например, при перегрузке операции + перегрузится и операция +=). Некоторые операции могут быть перегружены только парами: == и!=, > и <, >= и <=, true и false.

Для перегрузки операций используется специальный статический метод, имя которого образовано из ключевого слова operator и знака операции. Количество формальных параметров метода зависит от типа операции: унарная операция требует одного параметра, бинарная – двух. Метод обязательно должен иметь модификатор доступа public.

Рассмотрим перегрузку операций на примере. Определим класс для представления точек на плоскости с перегруженной операцией сложения:

public class Point

{

public double X { get; set; }

public double Y { get; set; }

public override string ToString()

{

return String.Format("X = {0} Y = {1}", X, Y);

}

public static Point operator +(Point a, Point b)

{

// для простоты не делаем проверку аргументов на null

return new Point {X = a.X + b.X, Y = a.Y + b.Y};

}

}

Для объектов класса Point возможно использование следующего кода:

var p1 = new Point {X = 10, Y = 20};

var p2 = new Point {X = -5, Y = 10};

Console.WriteLine(p1); // печатает "X = 10 Y = 20"

Console.WriteLine(p2); // печатает "X = -5 Y = 10"

Console.WriteLine(p1 + p2); // печатает "X = 5 Y = 30"

Параметры метода перегрузки должны быть параметрами, передаваемыми по значению. Тип формальных параметров и тип возвращаемого значения метода перегрузки обычно совпадает с описываемым типом, хотя это и не обязательное условие. Более того, класс или структура могут содержать версии одной операции с разным типом формальных параметров. Однако не допускается существование двух версий перегрузки операции, различающихся только типом возвращаемого значения. Также класс не может содержать перегруженной операции, у которой ни один из формальных параметров не имеет типа класса.

Дополним класс Point, позволив прибавлять к точке вещественное число:

public class Point

{

...

public static Point operator +(Point a, double delta)

{

return new Point {X = a.X + delta, Y = a.Y + delta};

}

}

Любой класс или структура могут перегрузить операции true и false. Операции перегружаются парой, тип возвращаемого значения операций – булев. Если выполнена подобная перегрузка, объекты могут использоваться как условия в операторах условного перехода и циклов.

Рассмотрим следующий пример. Пусть в классе Point перегружены операции true и false:

public class Point

{

...

public static bool operator true(Point a)

{

return (a.X > 0) || (a.Y > 0);

}

public static bool operator false(Point a)

{

// этот метод должен возвращать true,

// если семантика объекта соответствует false

return (a.X == 0) && (a.Y == 0);

}

}

Теперь возможно написать такой код (обратите внимание на оператор if):

var p = new Point {X = 10, Y = 20};

if (p)

Console.WriteLine("Point is positive");

else

Console.WriteLine("Point has non-positive data");

Если класс или структура T перегружают true, false и операции & или |, то становится возможным вычисление булевых выражений по сокращённой схеме. В этом случае:

– выражение x && y транслируется в T.false(x)? x: T.&(x,y);

– выражение x || y транслируется в T.true(x)? x: T.|(x,y);

Любой класс или структура могут перегрузить операции для неявного и явного приведения типов. При этом используется следующий синтаксис:

public static implicit operator целевой-тип(приводимый-тип имя)

public static explicit operator целевой-тип(приводимый-тип имя)

Ключевое слово implicit используется при перегрузке неявного приведения типов, а ключевое слово explicit – при перегрузке операции явного приведения. Либо целевой-тип, либо приводимый-тип должны совпадать с типом того класса, где выполняется перегрузка операций.

Поместим две перегруженных операции приведения в класс Point:

public class Point

{

...

public static implicit operator Point(double x)

{

return new Point {X = x};

}

public static explicit operator double(Point a)

{

return Math.Sqrt(a.X * a.X + a.Y * a.Y);

}

}

Вот пример кода, использующего преобразование типов:

var p = new Point {X = 3, Y = 4};

double y = 10;

double x = (double) p; // явное приведение типов

p = y; // неявное приведение типов

Хотелось бы отметить, что разработчик должен ответственно подходить к вопросу перегрузки операций. Методы перегрузки в совокупности с методами приведения типов могут породить код, который трудно сопровождать.

Анонимные типы

Анонимные типы (anonymous types), представленные в C# 3.0, позволяют создавать новый тип, не декларируя его заранее, а описывая непосредственно при создании переменной. Мотивом для введения анонимных типов в спецификацию языка послужила работа с коллекциями в технологии LINQ. При обработке коллекций тип элементов результата может отличаться от типа элементов исходной коллекции. Например, одна обработка набора объектов Student может привести к коллекции, содержащей имя студента и возраст. Другая обработка – к коллекции с именем и номером группы. В таких ситуациях в старых версиях C# нужно или заранее создать необходимое количество вспомогательных типов, или воспользоваться неким «мегатипом», содержащим все возможные поля результатов. Анонимные типы предлагают более элегантное решение.

Объявление анонимного типа использует синтаксис инициализатора объектов, предварённый ключевым словом new. Тип полей не указывается, а выводится из начального значения.

var anonymous = new {a = 3, b = true, c = "string data"};

Если при объявлении анонимного типа в качестве значений полей применяются не константы, а элементы известного пользовательского типа или локальные переменные, то имя поля анонимного типа можно не указывать. Будет использовано имя инициализатора.

int x = 10;

// у анонимного типа будут поля x (со значением 10), b и c

var anonymous = new {x, b = true, c = "string data"};

Анонимный тип следует рассматривать как класс, состоящий из полей только для чтения. Кроме полей, других элементов анонимный тип содержать не может. Два анонимных типа считаются эквивалентными, если у них полностью (вплоть до порядка) совпадают поля (имена и типы).

var anonymous = new {a = 3, b = true, c = "string data"};

var anotherAnonymous = new {a = 1, b = false, c = "data"};

anonymous = anotherAnonymous; // допустимое присваивание

Хотя анонимный тип задумывался как хранилище данных (концепция анонимных типов близка к концепции кортежей), действия в анонимный тип можно поместить, используя делегаты:

Action<int> m = x => Console.WriteLine(x);

var anonymous = new {data = 1, method = m};

anonymous.method(3);

25. Пространства имён

Пространства имён служат для логической группировки пользовательских типов. Применение пространств имён обосновано в крупных программных проектах для снижения риска конфликта имён и улучшения структуры библиотек кода.

Синтаксис описания пространства имён следующий:

namespace имя-пространства-имён

{

[компоненты-пространства-имён]

}

Компонентами пространства имён могут быть классы, интерфейсы, делегаты, перечисления, структуры и другие пространства имён. Само пространство имён может быть вложено только в другое пространство имён.

Если в разных местах программы определено несколько пространств имён с одинаковыми именами, компилятор собирает компоненты из этих пространств в общее пространство имён. Для этого необходимо, чтобы одноименные пространства имён находились на одном уровне вложенности в иерархии пространств имён.

Для доступа к компонентам пространства имён используется синтаксис имя-пространства-имён.имя-компонента.

Для использования в программе некоего пространства имён служит директива using. Её синтаксис следующий:

using имя-пространства-имён;

using [имя-псевдонима =] имя-пространства[.имя-типа];

Импортирование пространства имён позволяет сократить полные имена классов. Псевдоним, используемый при импортировании, это обычно короткий идентификатор для ссылки на пространство имён (или на элемент из пространства имён) в тексте программы. Импортировать можно пространства имён, как из текущего проекта, так и из подключённых к проекту сборок.

Рассмотрим некоторые тонкости при работе с пространствами имён. Предположим, что создаётся проект, использующий внешние сборки A.dll и B.dll. Пусть сборка A.dll содержит пространство имён NS с классом C, и сборка B.dll содержит такое же пространство и класс. Как поступить для доступа к различным классам C в коде? Эту проблему решает операция:: и директива extern alias. Во-первых, сборкам A.dll и B.dll нужно назначить текстовые псевдонимы. В Visual Studio псевдоним для подключённой сборки можно установить в свойствах сборки. При использовании компилятора командной строки псевдоним указывается с опцией ссылки на сборку:

csc.exe program.cs /r:A=A.dll /r:B=B.dll

Затем с элементами сборок можно работать следующим образом:

extern alias A;

extern alias B;

public class Program

{

private static void Main()

{

var a = new A::NS.C();

var b = new B::NS.C();

}

}

Существует предопределённый псевдоним с именем global для всех стандартных сборок платформы.NET.





Дата публикования: 2015-11-01; Прочитано: 366 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.01 с)...