Читайте также:
|
|
The List<T> class declares a single event member called Changed, which indicates that a new item has been added to the list. The Changed event is raised by the OnChanged virtual method, which first checks whether the event is null (meaning that no handlers are present). The notion of raising an event is precisely equivalent to invoking the delegate represented by the event—thus, there are no special language constructs for raising events.
Clients react to events through event handlers. Event handlers are attached using the += operator and removed using the -= operator. The following example attaches an event handler to the Changed event of a List<string>.
using System;
class Test
{
static int changeCount;
static void ListChanged(object sender, EventArgs e) {
changeCount++;
}
static void Main() {
List<string> names = new List<string>();
names.Changed += new EventHandler(ListChanged);
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
Console.WriteLine(changeCount); // Outputs "3"
}
}
For advanced scenarios where control of the underlying storage of an event is desired, an event declaration can explicitly provide add and remove accessors, which are somewhat similar to the set accessor of a property.
24. Обобщенные классы в языке C# и их отличие от шаблонов классов в языке C++. Установка ограничений на параметры обобщенных классов. Обобщенные делегаты.
Обобщенные классы в языке C# и их отличие от шаблонов классов в языке C++
Обобщенные типы присутствуют во многих библиотеках базовых классов.NET, но пространство имен System.Collections.Generic буквально наполнено ими. Подобно своему “родственнику” без обобщений (System.Collections), пространство имен System.Collections.Generic содержит множество типов класса и интерфейса, что позволяет вкладывать элементы в самые разные контейнеры.
ПРИМЕЧАНИЕ По соглашению для обобщенных типов их замещаемые параметры обозначаются буквами верхнего регистра. И хотя здесь допустимо использовать любые буквы (или слова), обычно используют T для обозначения типов, K — для ключей, а V — для значений. |
В пространстве имен System.Collections.Generic определяется и ряд классов, реализующих многие из этих ключевых интерфейсов.
Обобщенный класс | Необобщенный аналог в System.Collections | Описание |
Collection<T> | CollectionBase | База для обобщенной коллекции |
Comparer<T> | Comparer | Выполняет сравнение двух обобщенных объектов |
Dictionary<K, V> | Hashtable | Обобщенная коллекция пар имен и значений |
List<T> | ArrayList | Список элементов с динамически изменяемыми размерами |
Queue<T> | Queue | Обобщенная реализация списка FIFO (дисциплина обслуживания типа “очередь”) |
SortedDictionary<K, V> | SortedList | Обобщенная реализация сортированного набора пар имен и значений |
Stack<T> | Stack | Обобщенная реализация списка LIFO (дисциплина обслуживания типа “стек”) |
LinkedList<T> | — | Обобщенная реализация двусвязного списка |
ReadOnlyCollection<T> | ReadOnlyCollectionBase | Обобщенная реализация набора элементов только для чтения |
Тип List<T>
Подобно необобщенным классам, обобщенные классы являются объектами, размещаемыми в динамической памяти, поэтому для них следует использовать new со всеми необходимыми аргументами конструктора. Кроме того, вы должны указать типы, замещающие параметры, определенные обобщенным типом. Так, для System.Collections.Generic.List<T> требуется указать одно значение, задающее вид элемента, с которым будет функционировать List<T>. Например, чтобы создать три объекта List<> для хранения целых чисел, объектов SportsCar и объектов Person, вы должны записать следующее.
static void Main(string[] args){ // Создается List для хранения целых чисел. List<int> myInts = new List<int>(); // Создается List для хранения объектов SportsCar. List<SportsCar> myCars = new List<SportsCar>(); // Создается List для хранения объектов Person. List<Person> myPeople = new List<Person>(); } |
Когда вы создаете тип List<T> и указываете для него SportsCar, это эквивалентно следующему определению типа List<T>.
namespace System.Collections.Generic{ public class List<SportsCar>: IList<SportsCar>, ICollection<SportsCar>, IEnumerable<SportsCar>, IList, ICollection, IEnumerable {... public void Add(SportsCar item); public IList<SportsCar> AsReadOnly(); public int BinarySearch(SportsCar item); public bool Contains(SportsCar item); public void CopyTo(SportsCar[] array); public int FindIndex(System.Predicate<SportsCar> match); public SportsCar FindLast(System.Predicate<SportsCar> match); public bool Remove(SportsCar item); public int RemoveAll(System.Predicate<SportsCar> match); public SportsCar [] ToArray(); public bool TrueForAll(System.Predicate<SportsCar> match); public SportsCar this[int index] { get; set; }... }} |
// Этот метод переставляет любые два элемента, // определенные параметром типа <T>. static void Swap<T>(ref T a, ref T b) { T temp; temp = a; a = b; b = temp; } |
Swap<int>(ref a, ref b);
При вызове обобщенных методов, подобных Swap<T>, у вас есть возможность не указывать параметр типа, но только в том случае, когда обобщенный метод требует указания аргументов, поскольку тогда компилятор может “выяснить” тип этих аргументов на основе вводимых параметров.
// Компилятор будет предполагать System.Boolean. bool b1 = true, b2 = false; Console.WriteLine("До обмена: {0}, {1}", b1, b2); Swap(ref b1, ref b2); Console.WriteLine("После обмена: {0}, {1}", b1, b2); |
Но если, например, у вас есть обобщенный метод с именем DisplayBaseClass<T>, не имеющий входных параметров, как показано ниже:
static void DisplayBaseClass<T>(){ Console.WriteLine("Базовым классом {0} является: {1}.", typeof(T), typeof(T).BaseType); } |
то при вызове этого метода вы должны указать параметр типа.
В данном случае обобщенные методы Swap<T> и DisplayBaseClass<T> были определены в рамках объекта приложения (т.е. в рамках типа, определяющего метод Main()). Если вы предпочтете определить эти члены в новом типе класса (MyHelperClass), то должны записать следующее.
public class MyHelperClass{ public static void Swap<T>(ref T a, ref T b) { Console.WriteLine("Методу Swap() передано {0}", typeof(T)); T temp; temp = a; a = b; b = temp; } public static void DisplayBaseClass<T>() { Console.WriteLine("Базовым классом {0} является: {1}.", typeof(T), typeof(T).BaseType); }} |
Обратите внимание на то, что тип MyHelperClass сам по себе не является обобщенным, но определяет два обобщенных метода. Так или иначе, теперь, когда методы Swap<T> и DisplayBaseClass<T> находятся в контексте нового типа класса, при вызове их членов придется указать имя типа.
MyHelperClass.Swap<int>(ref a, ref b); |
Наконец, обобщенные методы не обязаны быть статическими. Если бы Swap<T> и DisplayBaseClass<T> были методами уровня экземпляра, нужно было бы просто создать экземпляр MyHelperClass и вызвать их из объектной переменной.
MyHelperClass c = new MyHelperClass(); c.Swap<int>(ref a, ref b); |
// Обобщенная структура Point.public struct Point<T>{ // Обобщенные данные. private T xPos; private T yPos; // Обобщенный конструктор. public Point(T xVal, T yVal) { xPos = xVal; yPos = yVal; } // Обобщенные свойства. public T X { get { return xPos; } set { xPos = value; } } public T Y { get { return yPos; } set { yPos = value; } } public override string ToString() { return string.Format("[{0}, {1}]", xPos, yPos); } // Переустановка полей со значениями параметра типа, // принятыми по умолчанию. public void ResetPoint() { xPos = default(T); yPos = default(T); }} |
В C# 2005 ключевое слово default получило два значения. Кроме использования в конструкции switch, оно может использоваться для установки параметрам типа значений, принятых по умолчанию. И это, очевидно, полезно, поскольку обобщенный тип ничего заранее не знает о фактических замещающих значениях и поэтому не может с безопасностью предполагать о том, каким должно быть значение по умолчанию. Значения по умолчанию для параметра типа являются следующими.
Дата добавления: 2015-11-16; просмотров: 83 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Частично определяемые классы и их назначение | | | Создание пользовательских обобщенных коллекций |