Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ответы ОСиСП.doc
Скачиваний:
71
Добавлен:
11.05.2015
Размер:
1.78 Mб
Скачать

Обобщенные типы и наследование

Обобщенный тип, как и всякий другой, может быть производным от других типов. При использовании обобщенного типа с указанием аргументов-типов в CLR определяется новый объект-тип, производный от того же типа, что и обобщенный тип. Например, List<T> является производным от Object, поэтому List<String> и List<Guid> тоже производные от Object. Аналогично, DictionaryStringKey<TValue> — производный от Dictionary<String, TValue>, поэтому DictionaryStringKey<Guid> также производный от Dictionary<String, Guid>. Понимание того, что определение аргументов-типов не имеет ничего общего с иерархиями наследования, позволяет разобраться, какие приведения типов допустимы, а какие нет.

Например, пусть класс Node связного списка определяется следующим образом.

internal sealed class Node<T>

{

public T m_data;

public Node<T> m_next;

public Node(T data) : this(data, null) { }

public Node(T data, Node<T> next)

{

m_data = data;

m_next = next;

}

public override String ToString()

{

return m_data.ToString() + ((m_next != null) ? m_next.ToStringO : null);

}

}

Тогда код для создания связного списка будет примерно таким:

private static void SameDataLinkedList()

{

Node<Char> head = new Node<Char>('C');

head = new Node<Char>('B', head);

head = new Node<Char>('A', head);

Console.WriteLine(head.ToString());

}

В приведенном выше классе Node поле m_next должно ссылаться на другой узел, поле m_data которого содержит тот же тип данных. Это значит, что узлы связного списка должны иметь одинаковый (или производный) тип данных. Например, нельзя использовать класс Node для создания связного списка, в котором тип данных одного элемента — Char, другого — DateTime, а третьего — String.

Однако, определив необобщенный базовый класс Node, а затем — обобщенный класс TypedNode (используя класс Node как базовый), можно создать связный список с произвольным типом данных у каждого узла. Приведу определения новых классов.

internal class Node

{

protected Node m_next;

public Node(Node next)

{

m_next = next;

}

}

internal sealed class TypedNode<T> : Node

{

public T m_data;

public TypedNode(T data) : this(data, null) {}

public TypedNode(T data, Node next) : base(next)

{

m_data = data;

}

public override String ToString()

{

return m_data.ToString() + ((m_next != null) ? m_next.ToString() : null);

}

}

Теперь можно написать код для создания связного списка с разными типами данных у разных узлов. Код будет примерно таким.

private static void DifferentDataLinkedList()

{

Node head = new TypedNode<Char>('.');

head = new TypedNode<DateTime>(DateTime.Now, head);

head = new TypedNode<String>("Today is ", head);

Console.WriteLine(head.ToString());

}

Проблемы с идентификацией и тождеством обобщенных типов

Синтаксис обобщенных типов часто приводит разработчиков в замешательство. Для упрощения синтаксиса некоторые разработчики определяют новый необобщенный тип класса, производный от обобщенного типа и определяющий все необходимые аргументы-типы. Например, если нужно упростить следующий код:

List<DateTime> dt = new List<DateTime>();

некоторые разработчики сначала определят класс, вот так:

internal sealed class DateTimeList : List<DateTime>

{

// Здесь никакой код добавлять не нужно!

}

Теперь код, создающий список, можно написать проще:

DateTimeList dt = new DateTimeList();

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

Boolean sameType = (typeof(List<DateTime>) == typeof(DateTimeList));

При выполнении этого кода sameType инициализируется значением false, потому что сравниваются два объекта разных типов. Это также значит, что методу, в прототипе которого определено, что он принимает значение типа DateTimeList, нельзя передать List<DateTime>. Но методу, который должен принимать List<DateTime>, можно передать DateTimeList, потому что DateTimeList является производным от List<DateTime>. Запутаться в этом очень просто.

C# позволяет использовать упрощенный синтаксис для ссылки на обобщенный закрытый тип, не влияя на эквивалентность типов. Для этого в начало файла с исходным текстом нужно добавить старую добрую директиву using, вот так:

using DateTimeList = System.Collections.Generic.List<System.DateTime>;

Здесь директива using просто определяет символ DateTimeList. При компиляции кода компилятор заменяет все DateTimeList на System.Collections.Genericlist <SystemDateTime>. Таким образом, разработчики могут использовать упрощенный синтаксис, не влияя на смысл кода и тем самым сохраняя идентификацию и тождество типов. И теперь при выполнении следующей строки кода sameType инициализируется true.

Boolean sameType = (typeof(List<DateTime>) == typeof(DateTimeList));