Главная · Карта сайта · Поиск · Статьи · Компьютерные курсы · Обучающие программы · Открытые проекты · Веб-программирование · Создание интернет-сайта · Полезные ссылки · Глоссарий · Контакты · Декабрь 09 2016 22:16:55
Последнее опубликованное

Что такое Model-View-Controller
Pattern Model-View-Controller

Как создать свой веб-сайт
Как создать свой сайт в интернете

Разное
Статистика

Программирование на C#. Делегаты


[Назад] [Следующая страница]

4.2. Универсальные делегаты и контрвариантность

Как уже было сказано в предыдущем разделе, делегаты тоже могут быть универсальными. Попытаемся развить функциональность класса GeometrySet, который появился в предыдущем разделе: обеспечим возможность гибкой настройки графического представления геометрии. Ранее мы уже описали метод Draw(), получающий на вход реализацию интерфейса IDrawContext. Расширим эту функциональность добавлением нескольких методов CustomDrawXXX(), которые, помимо ссылки на IDrawContext, будут получать ссылку на реализующий логику рисования графического примитива метод. Логика формирования графического представления объектов будет зависеть как от типа геометрических объектов коллекции, так и от типа графического стиля.

Графический стиль – это абстракция, инкапсулирующая в себе параметры графического представления геометрических объектов на карте. Набор параметров графического стиля определяется типом геометрии: точечная, линейная, площадная, текстовая и т.д. К основным параметрам графического стиля относят цвет, толщину линий, тип и размер шрифта и т.п. Стоит заметить, что полигон, являющийся основным представителем площадной геометрии, может быть изображен как в соответствии со стилем линейной геометрии, так и в соответствии со стилем площадной геометрии. В случае применения стиля линейной геометрии существует возможность определить только параметры графического представления границы полигона. Использование стиля площадной геометрии, помимо границы, позволит задать параметры “заливки” контура. Иерархия классов геометрии и графических стилей приведена ниже:

Диаграмма классов Geometry2D и GeometryStyle
Диаграмма классов графических примитивов и стилей.

Расширенный вариант GeometrySet и отдельный класс, реализующий специальную логику формирования графического представления объектов - MyCustomDrawing изучаем на следующем примере кода.

using System;
using System.Collections.Generic;
namespace CSharpQuickGuide
{
    //Ломаная линия
    public class PolylineGeometry : Geometry2D
    {
        //Конструктор по умолчанию
        public PolylineGeometry() : base(EGeometryType.gtLinear) { }   
        //Реализация абстрактного метода Clone()
        public override Geometry2D Clone()
        { throw new NotImplementedException(); }
    }
    //Полигон
    public class PolygonGeometry : Geometry2D
    {
        //Конструктор по умолчанию
        public PolygonGeometry() : base(EGeometryType.gtArea) { }
        //Реализация абстрактного метода Clone()
        public override Geometry2D Clone()
        { throw new NotImplementedException(); }
    } 
    //Графические стили отображения геометрии
    public class GeometryStyle {}
    public class PointGeometryStyle : GeometryStyle { }
    public class LinearGeometryStyle : GeometryStyle { }
    public class AreaGeometryStyle : GeometryStyle { }
    public class TextPointGeometryStyle : PointGeometryStyle { }

    //Коллекция геометрических объектов (расширенная версия)
    public class GeometrySet<TItem, TBaseContainer>
        : IEnumerable<TItem>, ICollection<TItem>
        where TItem : Geometry2D //TItem обязан быть производным от Geometry2D
        where TBaseContainer : IEnumerable<TItem>, ICollection<TItem>, new()
    {
        //Базовый контейнер
        private TBaseContainer m_Container;
        //Конструктор по умолчанию
        public GeometrySet()
        {
            m_Container = new TBaseContainer();
        }
        //Метод вывода геометрии коллекции по умолчанию
        public virtual void Draw<TContext>(TContext _context)
            where TContext : class, IDrawContext
        { throw new NotImplementedException(); }
        //Дополнительные методы вывода геометрии коллекции по произвольным (внешним) правилам
        //и с произвольным графическим стилем. Правила (логика) 'рисования' определяются методом,
        //который передается в качестве параметра _howToDraw, и сигнатура которого должна соответствовать 
        //универсальному делегату DrawAs.
        //1. Вывод геометрии любого типа
        public virtual void CustomDraw<TContext>(DrawAs<Geometry2D, GeometryStyle, TContext> _howToDraw,
 GeometryStyle _style, TContext _context)
            where TContext : class , IDrawContext
        {
            foreach (Geometry2D _geometry in this)
                _howToDraw(_geometry, _style, _context);
        }
        //2. Вывод только линейной геометрии
        public virtual void CustomDrawAsLinear<TContext>(DrawAs<Geometry2D, LinearGeometryStyle, TContext> _howToDraw,
 LinearGeometryStyle _style, TContext _context)
            where TContext : class, IDrawContext
        {
            foreach (Geometry2D _geometry in this)
                if (_geometry.Type == EGeometryType.gtLinear)
                    _howToDraw(_geometry, _style, _context);
        }
        //3. Вывод только площадной геометрии
        public virtual void CustomDrawAsArea<TContext>(DrawAs<Geometry2D, AreaGeometryStyle, TContext> _howToDraw,
 AreaGeometryStyle _style, TContext _context)
            where TContext : class, IDrawContext
        {
            foreach (Geometry2D _geometry in this)
                if (_geometry.Type == EGeometryType.gtArea)
                    _howToDraw(_geometry, _style, _context);
        }
        //4. Вывод текста
        public virtual void CustomDrawText<TContext>(DrawAs<Label, TextPointGeometryStyle, TContext> _howToDraw,
 TextPointGeometryStyle _style, TContext _context)
            where TContext : class, IDrawContext
        {
            foreach (Geometry2D _geometry in this)
                if (_geometry.Type == EGeometryType.gtPoint)
                    _howToDraw(_geometry as Label, _style, _context);
        }
        //Объявление универсального делегата вывода графики отдельного элемента пространственных данных
        public delegate void DrawAs<in TGeometry, in TStyle, TContext>(TGeometry _geometry, TStyle _style, TContext _context)
            where TGeometry : Geometry2D
            where TStyle : GeometryStyle; 
   }
    //Класс, реализующий специальную логику формирования графического представления пространственных данных
    public static class MyCustomDrawing
    {
        //Вывод графического представления элементов любого типа
        public static void DrawAny<TContext>(Geometry2D _geometry, GeometryStyle _style, TContext _context)
        { throw new NotImplementedException(); }
        //Вывод только линейной геометрии
        public static void DrawLinear<TContext>(Geometry2D _geometry, LinearGeometryStyle _style, TContext _context)
        { throw new NotImplementedException(); }
        //Вывод только площадной геометрии
        public static void DrawArea<TContext>(Geometry2D _geometry, AreaGeometryStyle _style, TContext _context)
        { throw new NotImplementedException(); }
        //Вывод только текста
        public static void DrawText<TContext>(Label _geometry, TextPointGeometryStyle _style, TContext _context)
        { throw new NotImplementedException(); }
    }
}

Далее приведены примеры корректных и некорректных вызовов методов CustomDrawXXX, включая пример с контрвариантностью. Понятия ковариантности и контрвариантности в контексте универсальных типов описаны в здесь.

using System;
using System.Collections.Generic;
namespace CSharpQuickGuide
{
    public static class SampleDraw
    {
        public static void Invoke(IDrawContext _drawContext)
        {
            //Создание коллекции пространственных данных
            GeometrySet<Geometry2D , LinkedList<Geometry2D>> _set = new GeometrySet<Geometry2D, LinkedList<Geometry2D>>();       
            //Формирование коллекции
            //...
            //Корректные вызовы CustomDraw
            _set.CustomDraw<IDrawContext>(MyCustomDrawing.DrawAny, new GeometryStyle(), _drawContext);
            _set.CustomDrawAsLinear<IDrawContext>(MyCustomDrawing.DrawLinear, new LinearGeometryStyle(), _drawContext);
            _set.CustomDrawAsArea<IDrawContext>(MyCustomDrawing.DrawArea, new AreaGeometryStyle(), _drawContext);
            _set.CustomDrawText<IDrawContext>(MyCustomDrawing.DrawText, new TextPointGeometryStyle(), _drawContext);
            //Контрвариантность (вызов корректный): передача менее специализированной
            //версии метода в сравнении с ожидаемой DrawAs<Label, TextPointGeometry>
            _set.CustomDrawText<IDrawContext>(MyCustomDrawing.DrawAny, new TextPointGeometryStyle(), _drawContext);     
            //Некорректный вызов, поскольку передаваемый метод DrawText
            //имеет более узкую (специализированную) ОДЗ, нежели ожидаемый DrawAs<Geometry2D, GeometryStyle>.
            _set.CustomDraw<IDrawContext>(MyCustomDrawing.DrawText, new GeometryStyle(), _drawContext);      
        }
    }
}

Использование же ковариантности продемонстрируем на примере фабрики геометрических объектов - GeometryFactory. Здесь все просто: определяем универсальный делегат создания геометрии CreateGeometry(), возвращающий экземпляр геометрии, и набор статических методов, сигнатура которых соответствует нашему универсальному делегату. Собственно, все необходимые комментарии в примере:

using System;
using System.Collections.Generic;
namespace CSharpQuickGuide
{
    //Фабрика геометрических объектов
    public static class GeometryFactory
    {
        //Делегат - функция создания геометрического объекта
        public delegate TGeometry CreateGeometry<out TGeometry>(string _parameters)
            where TGeometry : Geometry2D;
        //Создание полигона
        public static PolygonGeometry CreatePolygon(string _parameters)
        { throw new NotImplementedException(); }
        //Создание ломаной линии
        public static PolylineGeometry CreatePolyline(string _parameters)
        { throw new NotImplementedException(); }
        //Создание точечной геометрии
        public static PointGeometry CreatePoint(string _parameters)
        { throw new NotImplementedException(); }
        //Создание текстовой метки
        public static Label CreateText(string _parameters)
        { throw new NotImplementedException(); }
    }
    //Примеры создания экземпляров геометрии
    public static class SampleFactory
    {
        public static void Invoke(IDrawContext _drawContext)
        {
            //Примеры присвоения ссылок на методы фабрики
            //Полное соответствие сигнатуре делегата
            GeometryFactory.CreateGeometry<PolylineGeometry> _createPolyline = GeometryFactory.CreatePolyline;    
            //Ковариантность
            GeometryFactory.CreateGeometry<Geometry2D> _createGeometry = GeometryFactory.CreatePolygon;
            GeometryFactory.CreateGeometry<PointGeometry> _createPoint = GeometryFactory.CreateText;
            //Некорректные присовения: попытка передать ссылку на метод, возвращающий менее
            //специализированный объект
            GeometryFactory.CreateGeometry<Label> _createLabel1 = GeometryFactory.CreatePoint;
            GeometryFactory.CreateGeometry<Label> _createLabel2 = _createPoint;

            //Примеры создания геометрии методами фабрики
            PolylineGeometry _newPolyline = _createPolyline("(55.5;49.3)(72.1;24.8)(84.5;35.7)");
            Geometry2D _newPolygon = _createGeometry("(12.2;44.3)(48.1;55.2)(44.5;39.7)(15.1;29.8)");

            //Пример добавление нескольких ссылок на функции одному и тому же делегату
            //Такое присвоение приведет к тому, что дальнейший вызов _createGeometry последовательно вызовет
            //методы CreatePolygon и CreatePolyline
            _createGeometry += GeometryFactory.CreatePolyline;
            //Ссылка на создаваемый в ходе выполнения GeometryFactory.CreatePolygon (первый в списке делегата _createGeometry) 
            //объект будет потеряна, поскольку в качестве результата мы получим результат выполнения последней 
            //присвоенной функции: GeometryFactory.CreatePolyline
            Geometry2D _newPolyline2 = _createGeometry("(22.1;27.4)(47.3;25.1)(45.1;79.5)(24.2;28.7)"); 
        }
    }
}

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



[Назад] [Следующая страница]

Компьютерные курсы и курсы программирования
Основы программирования

Курс для начинающих программистов на C# и VB.NET.

SQL 25™

Построение SQL запросов и работа с базой данных.

C# Quick Guide™

Программирование на C#. Краткое руководство.

RegEx

Применение регулярных выражений.

Plug-in архитектура

Примеры программной Plug-in архитектуры.

XML и его расширения

Язык разметки XML и его расширения с примерами.

HTML и разметка гипертекста

Языки HTML, XHTML и CSS с примерами разметки.

Основы веб-дизайна

Основы веб-дизайна: решения типовых задач верстки.

Программирование на PHP

Руководство по программированию на PHP для начинающих.

Справочные материалы

Шаблоны проектирования
Каталог шаблонов проектирования программных компонентов.

Рефакторинг кода
Каталог приемов рефакторинга программного кода.

Гость
Имя

Пароль



Забыли пароль?
Запросите новый здесь
.
Coding Craft. Все права защищены © 2011. Проект Инициативного Народного Фронта Образования - ИНФО-проект.