Про опции /debug /optimize компилятора C#

Категории: Разработка ПО
Тэги: ,

Неплохая статейка, описывающая роли ключей /debug /optimize, компилятора C#:

http://blogs.msdn.com/ericlippert/archive/2009/06/11/what-does-the-optimize-switch-do.aspx

По следам PDC. C# 4.0 Features. Dynamycally Typed Objects.

Категории: Разработка ПО
Тэги: , , ,

Небольшой обзор того, что нам готовит новая версия спецификации языка.
Смена первой цифры произошла благодаря четырём основным фичам:

  • Dynamycally Typed Objects
  • Optional and Named Prameters
  • Improved COM Interoperability
  • Co- and Conra-variance

Сегодня подробней рассмотрим первую.

Dynamycally Typed Objects
Динамическая система типов является, пожалуй, самым основным нововведением языка. Судя по лекциям с PDC 2008, c точки зрения архитектуры .NET добавился еще один компонент – DLR (dynamic object runtime), являющийся, по сути, надстройкой над CLR. С точки зрения языка добавилось ключевое слово dymanic. И одноименный тип добавился в .NET Type System.
Какое время назад Microsoft Research утверждала, что реализация будет происходить так: на момент компиляции объект будет не типизирован, а тэгирован, и на момент выполнения тег заменяется на свежевычесленный тип и вместо “неопределенного” типа подставляется “настоящий” и дальше все работает по старой схеме. Собственно DLR этим и должен заниматься.
Но что же происходит на самом деле?
Read the rest of this entry »

Прекращение подачи видеосигнала на дисплей

Категории: Программы, Разработка ПО
Тэги: , , ,

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

За все отвечает системное сообщение WM_SYSCOMMAND с параметрами SC_MONITORPOWER и 2. LPARAM со значением 2 соответствует отключению дисплея. Значение параметра -1 – включает дисплей, 1 – вводит дисплей в режим Stand By (в основном актуально для ноутбуков).

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

static class Program
{
    [DllImport("user32.dll")]
    static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
 
    static IntPtr SC_MONITORPOWER = new IntPtr(0xF170);
    const uint WM_SYSCOMMAND = 0x0112;
    static IntPtr MONITOR_OFF = new IntPtr(2);
    static IntPtr HWND_BROADCAST = new IntPtr(0xffff);
 
    [STAThread]
    static void Main()
    {
        SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_OFF);
    }
}

User interface. Мысли вслух.

Категории: Разработка ПО
Тэги: , , ,

Позволю себе немного порассуждать на тему реализации UI обычных десктопных приложений.

Во-первых, разговоров о том, что UI правильнее всего описывать с помощью конечных автоматов была тьма-тьмущая. В целом это очень верно – логика работы заранее продумана и отточена. В каждый момент времени презентационная часть программы может находится в одном из состояний и, посредством манипуляции пользователем доступных контролов, переходить в одно из возможных целевых состояний. При грамотном построении автомата на этапе проектирования, сбоев в работе быть не должно. Потому автомат как паттерн поведения для “витрины” приложения наиболее гуманный выбор.

Во-вторых, при реализации GUI я предпочитаю пользоваться парадигмой MVP (Model-View-Presenter). Почему не MVC? Потому, что лично мне MVC (Model-View-Control) нравится гораздо меньше. Одно из основных различий этих подходов как раз и позволяет мне сделать однозначный выбор. Я считаю, что выводить половину функциональности презентационной части из диалога с пользователем стратегически неверно. Т.е. ответ от приложения приходит в окно, а весь feedback от пользователя идет контроллеру, тем самым заставляя его решать задачи, с которыми бы отлично справилось окно. Но самое интересное о применении MVP я опишу в далее.

В-третьих, есть мнение, что контроллеру (в случае MVC) или презентору (в случае MVP) совершенно неважно нажал ли пользователь кнопку, поменял ли значение комбобокса или повазюкал слайдер, ему должно все приходить в терминах некоторой внутренней структуры. Т.е., по-хорошему, между котроллером/презентором и view должен быть некоторый метауровень предназначенный для преобразования сущностей из одного архитектурного слоя в другой. Вот тут нам и приходит на помощь подход MVP, изолированность пользователя от презентера не вынуждает “рассказывать” ему об особенностях работы пользователя. Его не надо знакомить с интерфейсам в целом к его работе не имеющим отношения. Пользователю в целом предоставлена свобода действий в рамках интерактивного окна (или набора окон), все его действия ограничиваются манипуляциями с контролами на форме.

В-четвертых, при реализации автомата, одним из самых применимых шаблонов является шаблон Visitor. Потому при создании слоя для преобразования хэндлеров контролов в термины модели приложения необходимо четко продумать интерфейсы, о них я напишу ниже.

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

Подводя итог всему вышеперечисленному, позволю себе привести пример реализации небольшого приложения.

Вот так для презентора должна выглядеть логика работы UI:

Для начала определим делегаты и интерфейс с ними согласующийся. Вообще говоря часто можно обойтись двумя типами с параметром и без:

delegate void Action_WO_EventArgumentHandler();
delegate void Actoin_W_EventArgumentHandler<T> (T ea);
 
interface IUserActions
{
    event Action_WO_EventArgumentHandler OnUserWantsSomething;
    event Actoin_W_EventArgumentHandler<DateTime> OnUserWantsDateTime;
}

Использование generic добавляет известную гибкость при определении типа параметра.

Далее добавляем интерфейс для управления текущим состоянием контрола:

interface IStateSetters
{
    void SetContentForStateOne();
    void SetContentForStateTwo();
}

Теперь в теле класса нашего контрола реализуем оба интерфейса:

Далее со всеми необходимыми контролами поступаем схожим образом.

class FEControl1 : Control, IStateSetters, IUserActions
{
    public FEControl1()
        : base()
    {
        // Конструктор-шманструктор
    }
 
    public void SetContentForStateOne()
    {
        // Делаем вское c содержимым контрола для перевода его в StateOne
    }
 
    public void SetContentForStateTwo()
    {
        // Делаем вское c содержимым контрола для перевода его в StateTwo
    }
 
    public event Action_WO_EventArgumentHandler OnUserWantsSomething;
    public void OnClickAction()
    {
        if (OnUserWantsSomething != null)
            OnUserWantsSomething();
        // Обрабатываем вское, вызываем события контрола и т.п.
    }
 
    public event Actoin_W_EventArgumentHandler<DateTime> OnUserWantsDateTime;
    public void OnOtherClickAction()
    {
        if (OnUserWantsDateTime != null)
            OnUserWantsDateTime(DateTime.Now);
        // Обрабатываем вское, вызываем события контрола и т.п.
    }
}

Теперь наша задача реализовать Visitor

class FEControlVisitor
{
    List<IStateSetters> m_lstStateChangerList = new List<IStateSetters>();
 
    public void SetState()
    {
        foreach (IStateSetters st in m_lstStateChangerList)
        {
            if(ProgramData.State == States.StateOne)
                st.SetContentForStateOne();
            else if(ProgramData.State == States.StateTwo)
                st.SetContentForStateTwo();
        }
    }
}

Тут полет фантазии практически неограниченный. ProgramData, например, может быть Singleton, States – enum. ProgramData можно передавать в качестве параметра функции. Для опередления состояния можно устроить каскадный if, можно устроить switch, в зависимости от иерархии состояний. А можно вообще для каждого состояния держать свою функцию, в которой обходится лист подписанных на Visitor контролов.

Для случая подписки на события все происходит по схожему сценарию. Визитор обходит подписчиков и добавляет в хэндлер эвента метод, который будет его обрабатывать. Процедура в целом схожая с SetState().

Как мне кажется, описаный подход наиболее действенный и срабатывал неоднократно. Прекрасная масштабируемость в плане добавления котнролв, измемения поведения контролов, ну вообще одни плюсы!

Полиморфизм, абстрактные классы и интерфейсы

Категории: Разработка ПО
Тэги: ,

Рассмотрим реализацию полиморфизма в С#. Итак есть два основных способа, первый, посредством абстрактных классов, второй, посредством интерфейсов. Напомню свойства каждого из них.

Абстрактные классы

Пример реализации:

abstract class CFile
{
    public abstract void Open();
    public abstract void Save();
}
 
class CFileNewtype : CFile
{
    public override void Open()
    {
        Console.WriteLine("Open method for Newtype File");
    }
 
    public override void Save()
    {
        Console.WriteLine("Save method for Newtype File");
    }
}
 
class CFileOldtype : CFile
{
    public override void Open()
    {
        Console.WriteLine("Open method for Oldtype File");
    }
 
    public override void Save()
    {
        Console.WriteLine("Save method for Oldtype File");
    }
}

Абстрактные классы имеют ряд свойств:

  • Такие классы не могут быть sealed.
  • Они не могут быть инстанциированы оператором new.
  • При объявлении метода класса абстрактным подразумевается что он виртуальный, потому метод не может быть объявлен как virtual.
  • Все абстрактные члены должны быть определены в наследующем классе, за исключение случая, когда наследующий класс так же абстрактный.
  • Абстрактные методы класса не могут объявлены как static.
  • При реализации абстрактным классом интерфейса, все члены интерфейса должны быть либо реализованными, либо описаны их сигнатуры и объявлены как abstract.
  • Абстрактный класс может иметь как абстрактных так и не абстрактных членов, вообще говоря ограничений на количество абстрактных членов нет, т.е. их может не быть совсем, но тогда смысл объявления класса абстрактным теряется.
  • Объявлены абстрактными могут быть методы, свойства, индексаторы и события.
  • Абстрактный класс может реализовывать несколько интерфейсов и может быть наследником класса, при этом он может переопределять виртуальные методы неабстрактного класса.
  • Наследующий класс при переопределении абстрактного свойства обязан реализовывать один из аксессоров (либо “сеттер” либо “геттер”). Если переопределяется абстрактное свойство имеющее только один аксессор, то переопределяться может только он. Если абстрактное свойство имеет оба аксессора, то переопределять может как один так и оба.

Интерфейсы

Пример реализации:

interface IFileTypesProvider
{
    void Open();
    void Save();
}
 
class CFileNewtype : IFileTypesProvider
{
    public void Open()
    {
        Console.WriteLine("Open method for Newtype File");
    }
 
    public void Save()
    {
        Console.WriteLine("Save method for Newtype File");
    }
}
 
class CFileOldtype : IFileTypesProvider
{
    public void Open()
    {
        Console.WriteLine("Open method for Oldtype File");
    }
 
    public void Save()
    {
        Console.WriteLine("Save method for Oldtype File");
    }
}

Свойства интерфейсов:

  • Класс может реализовывать более одного интерфейса.
  • Интерфейс не содержит реализации методов.
  • Метод класса реализующий метод интерфейса должен быть не статическим, объявленным со спецификатором доступа public, иметь ту же сигнатуру.
  • Реализующий интерфейс класс может не придерживаться политики реализации аксессоров свойств интерфейса, за исключением ситуации, когда интерфейс объявлен как explicit.
  • Интерфейс не может быть инстанциирован оператором new.
  • Интерфейсы могут содержать методы, свойства, индексаторы и события.

Так когда стоит применять реализацию полиморфизма через абстрактные классы, а когда через интерфейсы?

  • Учитывая отсутствие множественного наследования, реализация нескольких интерфейсов спасает положение.
  • Абстрактные классы не подходят для value types. Таким образом полиморфизм для структур только через интерфейсы.
  • Абстрактные классы могут содержать реализацию внутренних, не абстрактных методов без нарушения правил наследования, однако добавление нового абстрактного метода нарушит все связи с наследниками, в этом случае, поможет добавление нового интерфейса.