Студопедия

КАТЕГОРИИ:

АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника


Асинхронный вызов делегатов.





 

28. Средства обобщенного (generic) программирования в языках C++ и C#.

Общие типы (Generics)

Обобщения (generics) —механизм, поддерживаемый общеязыковой исполняющей средой (CLR) и языками программирования, который является новой формой повторного использования кода, а именно повторным использованием алгоритма.

Разработчик определяет алгоритм, например сортировку или преобразование, но не указывает типы данных, с которыми тот работает. Поэтому алгоритм может обобщенно применяться к объектам разных типов. Используя готовый алгоритм, другой разработчик должен указать конкретные типы данных, например для алгоритма сортировки — Int32, а для алгоритма сравнения — DateTime.

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

 

[Serializable]

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

{

public List();

public void Add(T item);

public Int32 BinarySearch(T item);

public void Clear();

public Boolean Contains(T item);

public Int32 IndexOf(T item);

public Boolean Remove(T item);

public void Sort();

public void Sort(IComparer<T> comparer);

public void Sort(Comparison<T> comparison); public T[] ToArray();

public Int32 Count { get; }

public T this[Int32 index] { get; set; }

}

 

Выражением <T> сразу после имени класса автор класса List указал, что класс работает с неопределенным типом данных. При определении обобщенного типа или метода переменные, указывающие на типы (например, Т), называются параметрами-типами (type parameters). T — это имя переменной, которое применяется в исходном тексте во всех местах, где используется соответствующий тип данных.

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

Главные преимущества обобщений для разработчиков:

1. Защита исходного кода. Разработчику, использующему обобщенный алгоритм, не нужен доступ к исходному тексту алгоритма. А при работе с шаблонами С++ или обобщениями Java разработчику нужен был исходный текст алгоритма.

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

3. Более простой и понятный код. Поскольку компилятор обеспечивает безопасность типов, в исходном тексте нужно меньше приведений типов, и такой код проще писать и поддерживать. В последней строке SomeMethod разработчику не нужно использовать приведение (DateTime), чтобы присвоить переменной dt результат индексатора (при запросе элемента с индексом 0).

4. Повышение производительности. До появления обобщений одним из способов определения обобщенного алгоритма было определение всех его членов так, чтобы они по определению «умели» работать с типом данных Object. Чтобы этот алгоритм работал с экземплярами значимого типа, перед вызовом членов алгоритма CLR должна была упаковать этот экземпляр. В главе 5 показано, что упаковка требует выделения памяти в управляемой куче, что приводит к более частому сбору мусора, а это, в свою очередь, ухудшает производительность приложения. Поскольку отныне обобщенный алгоритм можно создавать для работы с конкретным значимым типом, экземпляры значимого типа могут передаваться по значению и CLR не нужно выполнять упаковку. Приведения типов также не нужны (см. предыдущий пункт), поэтому CLR не нужно проверять безопасность типов при их преобразовании, что также ускоряет работу кода.

Чтобы убедить вас в том, что обобщения повышают производительность, я написал программу для сравнения производительности необобщенного алгоритма ArrayList из библиотеки классов FCL и обобщенного алгоритма List.

 

using System;

using System.Collections;

using System.Collections.Generic;

using System.Diagnostics;

 

public static class Program

{

public static void Main()

{

ValueTypePerfTest();

ReferenceTypePerfTest();

}

 

private static void ValueTypePerfTest()

{

const Int32 count = 10000000;

using (new OperationTimer("List<Int32>"))

{

List<Int32> l = new List<Int32>(count);

for (Int32 n = 0; n < count; n++)

{

l.Add(n);

Int32 x = l[n];

}

l = null; // Это должно удаляться в процессе сбора мусора.

}

using (new OperationTimer("ArrayList of Int32"))

{

ArrayList a = new ArrayList();

for (Int32 n = 0; n < count; n++)

{

a.Add(n);

Int32 x = (Int32)a[n];

}

a = null; // Это должно удаляться в процессе сбора мусора.

}

}

 

private static void ReferenceTypePerfTest()

{

const Int32 count = 10000000;

using (new OperationTimer("List<String>"))

{

List<String> l = new List<String>();

for (Int32 n = 0; n < count; n++)

{

l.Add("X");

String x = l[n];

}

l = null; // Это должно удаляться в процессе сбора мусора.

}

using (new OperationTimer("ArrayList of String"))

{

ArrayList a = new ArrayList();

for (Int32 n = 0; n < count; n++)

{

a.Add("X");

String x = (String)a[n];

}

a = null; // Это должно удаляться в процессе сбора мусора.

}

}

}

// Это полезный способ оценки времени выполнения алгоритма.

internal sealed class OperationTimer : IDisposable

{

private Int64 m_startTime;

private String m_text;

private Int32 m_collectionCount;

 

public OperationTimer(String text)

{

PrepareForOperation();

m_text = text;

m_collectionCount = GC.CollectionCount(0);

// Это выражение должно быть последним в этом методе,

// чтобы обеспечить максимально точную оценку быстродействия.

m_startTime = Stopwatch.GetTimestamp();

}

 

public void Dispose()

{

Console.WriteLine("{0,6:###.00} seconds (GCs={1,3}) {2}",

(Stopwatch.GetTimestamp() - m_startTime) /

(Double)Stopwatch.Frequency, GC.CollectionCount(0) - m_collectionCount, m_text);

}

 

private static void PrepareForOperation()

{

GC.Collect();

GC.WaitForPendingFinalizers();

GC.Collect();

}

}

 

Скомпилировав эту программу в режиме Release (с включенной оптимизацией) и выполнив ее на своем компьютере, я получил такой результат:

.10 seconds (GCs= 0) List<Int32> 2.02 seconds (GCs= 30) ArrayList of Int32 .52 seconds (GCs= 6) List<String> .53 seconds (GCs= 6) ArrayList of String

Как видите, обобщенный алгоритм List с типом lnt32 работает гораздо быстрее, чем необобщенный алгоритм ArrayList с тем же типом. Кроме того, использование значимого типа (Int32) с ArrayList требует много операций упаковки, и, как результат, 30 сборок мусора, а в алгоритме List сбор мусора вообще не нужен.

Результаты проверки ссылочного типа не столь впечатляющие: временные показатели и число сборок мусора здесь примерно одинаковы. В данном случае у обобщенного алгоритма List реальных преимуществ нет.

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


Поделиться:

Дата добавления: 2015-04-21; просмотров: 66; Мы поможем в написании вашей работы!; Нарушение авторских прав





lektsii.com - Лекции.Ком - 2014-2024 год. (0.006 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты