Обобщенные методы

С# поддерживает обобщенные методы. Любое объявление метода внутри структуры, класса или интерфейса может быть сделано обобщенным. Сюда входят статические и виртуальные или абстрактные методы. К тому же в обобщенных или необобщенных типах можно объявлять обобщенные методы.

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

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

В следующем коде создается контейнер, в который будет добавлено содержимое другого обобщенного контейнера:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Collections.Generic;
public class MyContainer<t> : IEnumerable<t>
{
public void Add( T item )  {
impl .Add ( item ) ;
}
// Converter<tinput, TOutput> - новый тип делегата, введенный
// в .NET Framework 2.0, который может быть привязан к методу,
// знающему, как преобразовать тип TInput в тип TOutput.
public void Add<r> ( MyContainer<r> otherContainer, Converter<r, T> converter ) {
foreach( R item in otherContainer )  {
impl.Add( converter(item) );
}
public IEnumerator<t> GetEnumerator()  {
foreach ( T item in impl )  {
yield return item;
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator ();
}
private List<t> impl = new List<t>();
}
public class EntryPoint {
static void Main()  {
MyContainer<long> IContainer = new MyContainer<long>();
MyContainer<int> iContainer = new MyContainer<int>();
IContainer.Add( 1 );
IContainer.Add( 2 );
iContainer.Add( 3 );
iContainer.Add( 4 );
IContainer.Add( iContainer, EntryPoint.IntToLongConverter );
foreach ( long 1 in IContainer )  {
Console .WriteLine ( 1 ); }
}
static long IntToLongConverter ( int i )  {
return i;

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

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

Объекты, содержащиеся внутри экземпляра MyContainer, требовалось поместить в экземпляр MyContainer.

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

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

Но при этом следует принять во внимание, что обобщения формируются динамически во время выполнения. И во время выполнения нет никаких гарантий, какой именно закрытый тип, сформированный из MyContainer, увидит метод Add. Это может быть MyContainer, a Apples может и не быть неявно преобразуемым в long, предполагая, что он передан MyContainer. Add.

Те, кто использует шаблоны С++, догадаются, что такой трюк не сработает, поскольку компилятор уведомит о попытке некорректного преобразования во время компиляции. Однако обобщения лишены такой роскоши времени компиляции, поэтому для исключения подобной вещи предусмотрены дополнительные ограничения компиляции.

Придется поискать другое решение и предусмотреть делегат преобразования для выполнения такой работы.
Специально для этого случая библиотека базовых классов предлагает тип System.Converter

Когда кто-то вызывает Add, он может предоставить экземпляр обобщенного делегата Converter, указывающий метод, который знает, как преобразовать исходный тип в целевой. Это объясняет потребность в методе IntToLongConverter в предыдущем примере. Метод Add затем использует этот делегат для выполнения преобразования от одного типа к другому.

В данном случае преобразование неявно, но оно должно было быть вынесено наружу, поскольку во время компиляции компилятор должен учесть тот факт, что метод Add может принять любой тип.

В версии С# 4.0 появился новый синтаксис для пометки интерфейса или делегата ковариантным или контравариантным. В зависимости от ситуации, если типы т и R неявно преобразуются друг в друга, то определять делегат-конвертер, как показано выше, не обязательно.

Для облегчения перечисления контейнера также объявлен MyContainer, реализующий IEnumerable. Это позволит применять синтаксически интуитивно понятную конструкцию foreach. Некоторый синтаксис здесь может показаться незнакомым, если вы не имели дело с итераторами С#, особенно с оператором yield.

Обратите внимание, насколько легко создается перечислитель для этого класса с помощью ключевого слова yield.

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

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100