Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Построение специальных обобщенных методов представляет собой более развитую версию традиционной перегрузки методов. В главе 2 вы узнали, что перегрузка — это действие по определению нескольких версий одного метода, которые отличаются друг от друга количеством или типами параметров.

Хотя перегрузка является полезным средством объектно-ориентированного языка, проблема заключается в том, что при этом довольно легко получить в итоге огромное количество методов, которые по существу делают одно и то же. Например, пусть необходимо создать методы, которые позволяют менять местами два фрагмента данных посредством простой процедуры. Вы можете начать с написания нового статического класса с методом, который

способен оперировать целочисленными значениями:

using System;

namespace CustomGenericMethods

{

static class SwapFunctions

{

// Поменять местами два целочисленных значения.

static void Swap(ref int a, ref int b)

{

int temp = a;

a = b;

b = temp;

}

}

}

Пока все идет хорошо. Но теперь предположим, что нужно менять местами также и два объекта

Person
; действие потребует написания новой версии метода
Swap
:

// Поменять местами два объекта Person.

static void Swap(ref Person a, ref Person b)

{

Person temp = a;

a = b;

b = temp;

}

Вне всяких сомнений вам должно быть ясно, чем все закончится. Если также понадобится менять местами два значения с плавающей точкой, два объекта растровых изображений, два объекта автомобилей, два объекта кнопок или что-нибудь еще, то придется писать дополнительные методы, что в итоге превратится в настоящий кошмар при сопровождении. Можно было бы построить один (необобщенный) метод, оперирующий с параметрами типа

object
, но тогда возвратятся все проблемы, которые были описаны ранее в главе, т.е. упаковка, распаковка, отсутствие безопасности в отношении типов, явное приведение и т.д.

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

Swap<T>
, который способен менять местами два значения типа
Т
:

// Этот метод будет менять местами два элемента

// типа, указанного в параметре <Т>.

static void Swap<T>(ref T a, ref T b)

{

Console.WriteLine("You sent the Swap method a {0}", typeof(T));

T temp = a;

a = b;

b = temp;

}

Обратите внимание, что обобщенный метод определен за счет указания параметра типа после имени метода, но перед списком параметров. Здесь заявлено, что метод

Swap<T>
способен оперировать на любых двух параметрах
типа
<Т>
. Для придания некоторой пикантности имя замещаемого типа выводится на консоль с использованием операции
typeof
языка С#. Взгляните на показанный ниже вызывающий код, который меняет местами целочисленные и строковые значения:

Console.WriteLine("***** Fun with Custom Generic Methods *****\n");

// Поменять местами два целочисленных значения.

int a = 10, b = 90;

Console.WriteLine("Before swap: {0}, {1}", a, b);

SwapFunctions.Swap<int>(ref a, ref b);

Console.WriteLine("After swap: {0}, {1}", a, b);

Console.WriteLine;

// Поменять местами два строковых значения.

string s1 = "Hello", s2 = "There";

Console.WriteLine("Before swap: {0} {1}!", s1, s2);

SwapFunctions.Swap<string>(ref s1, ref s2);

Console.WriteLine("After swap: {0} {1}!", s1, s2);

Console.ReadLine;

Вот вывод:

***** Fun with Custom Generic Methods *****

Before swap: 10, 90

You sent the Swap method a System.Int32

After swap: 90, 10

Before swap: Hello There!

You sent the Swap method a System.String

After swap: There Hello!

Главное преимущество такого подхода в том, что придется сопровождать только одну версию

Swap<T>
, однако она в состоянии работать с любыми двумя элементами заданного типа в безопасной в отношении типов манере. Еще лучше то, что находящиеся в стеке элементы остаются в стеке, а расположенные в куче — соответственно в куче.

Выведение параметров типа

При вызове обобщенных методов вроде

Swap<T>
параметр типа можно опускать, если (и только если) обобщенный метод принимает аргументы, поскольку компилятор в состоянии вывести параметр типа на основе параметров членов. Например, добавив к операторам верхнего уровня следующий код, можно менять местами два значения
System.Boolean
:

// Компилятор выведет тип System.Boolean.

bool b1 = true, b2 = false;

Console.WriteLine("Before swap: {0}, {1}", b1, b2);

SwapFunctions.Swap(ref b1, ref b2);

Console.WriteLine("After swap: {0}, {1}", b1, b2);

Несмотря на то что компилятор может определить параметр типа на основе типа данных, который применялся в объявлениях

b1
и
b2
, вы должны выработать привычку всегда указывать параметр типа явно:

SwapFunctions.Swap<bool>(ref b1, ref b2);

Поделиться с друзьями: