1/17/2008

Пример использования IServiceProvider

Задача: надо передать текст из объекта №1 в объект №2 (определен в сборке №2 , а объект №1 в сборке №1). При передаче данных объект №1 указывает тип текста, например:

public enum TextType

{

    // для обработки можно использовать IHandler1 или IHandler2

    ShortText,

    // для обработки можно использовать IHandler3 или IHandler4

    LongText,

    // для обработки можно использовать IHandler5 или IHandler6

    SecretText,

}

 

А также объект №1 передает заголовок текста и должен предоставить объекту №2 доступ к соответствующим обработчикам IHandler*, которые объект №2 может использовать как ему вздумается или может вообще не использовать. При этом все IHandler* достаточно «тяжелые» и (главное) у них нет ничего общего!

 

Кривое решение: создаем Data Transfer Object (DTO):
public class DTO

{

    // ссылка на Object1 необходима, потому что именно Object1 знает где взять реализацию IHandler*

    internal DTO(Object1 owner)

    {

        _Owner = owner;

    }

    private Object1 _Owner;

   

    public string Title { get; internal set; }

    public DataType DataType { get; internal set; }

 

    // создается по-запросу

    public IHandler1 Handler1

    {

        get

        {

            if(_Handler1 != null)

                _Handler1 = _Owner.CreateHandler(typeof(IHandler1));

           

            return _Handler1;

        }

    }

    private IHandler1 _Handler1;

    /*

     * далее идут определения остальных IHandler*

     */

}

 

Минусы: 1) DTO «привязан» к Object1, т.е. при запросе одного из IHandler* надо обязательно обращаться к Object1;  2) много кода с определениями свойств.

 

Решение: создаем Data Transfer Object (DTO) и реализуем в нем IServiceProvider:

public class DTO : IServiceProvider

{

    // доступен для объекта №1, но недоступен для объекта №2

    internal ServiceContainer Services { get; set; }

 

    // доступен для объекта №2 через IServiceProvider

    object IServiceProvider.GetService(Type serviceType)

    {

        return (this.Services != null) ? this.Services.GetService(serviceType) : null;

    }

 

    public string Title { get; internal set; }

    public DataType DataType { get; internal set; }

}



Кода стало намного меньше и нет привязки к Object1. Т.е. мы полностью избавились от минусов кривого решения.
Пример использования:

 

DTO dto = new DTO() { DataType = DataType.ShortText, Services = new ServiceContainer() };

switch(dto.DataType)

{

    case DataType.ShortText:

        dto.Services.Add<IHandler1>((c, t) =>Factory1.CreateHandler());

        dto.Services.Add< IHandler2>((c, t) =>Factory2.CreateHandler());

        break;

    ...

}

 

Вопрос: откуда у DTO.Services появился метод Add<T>?
Ответ
: это Extension Method, см. ServiceModelHelpers.

1/16/2008

Исходники .NET Framework в Visual Studio 2008 (Debugging Support)

Спасибо Lutz Roeder'у и его Reflector’у, но теперь многие исходники .NET Framework можно смотреть прямо в VS 2008, а также ставить брейкпоинты и т.д.
См.
Releasing the Source Code for the .NET Framework Libraries, Configuring Visual Studio to Debug .NET Framework Source Code.

TCPView for Windows

TCPView for Windows позволяет увидеть информацию о TCP and UDP endpoints, включая локальные и удаленные адреса, а также состояние соединения и имя процесса, который держит endpoint.

Работает на Windows Server 2008, Vista, NT, 2000,  XP, 98, Me и 95 (если установить Windows 95 Winsock 2 Update). Есть версия Tcpvcon без UI (для работы в command-line).

Автор Марк Руссинович (Mark Russinovich, Sysinternals). Утилита совсем свежая J, выпущена: January 11, 2008. Подробнее ... (Скачать всего 94 Kb).

1/13/2008

Сокращаем код с помощью ServiceContainer и C# 3.0

Например, есть класс BaseClass , в котором отображается список каких-то элементов. Элементы можно выделять, редактировать их лейблы, удалять и т.д. Всю логику можно реализовать в отдельных сервисах. Выделение элементов определим в сервисе – ISelectionService, редактирование – в ILabelEditor, и т.п.
Чтобы воспользоваться этими сервисами в контроле надо определить свойства, например:


public class MyClass : BaseClass
{
public ISelectionService SelectionService
{
get
{
// сервис создается по-запросу, потому что он может вообще не пригодиться
if(_SelectionService == null)
_SelectionService = new MyControlSelectionService(this);
return _SelectionService;
}
}
private ISelectionService _SelectionService;

public ILabelEditor LabelEditor
{
get
{
// сервис создается по-запросу, потому что он может вообще не пригодиться
if(_LabelEditor == null)
_ LabelEditor = new MyControlLabelEditor(this);
return _LabelEditor;
}
}
private ILabelEditor _LabelEditor;

// пример использования сервисов
public void KeyDown(KeyDownEventArgs e)
{
switch(e.KeyCode)
{
case Keys.F2:
this.LabelEditor.BeginEdit(this.SelectedItem);
break;

case Keys.F5:
this.SelectionService.SelectAll();
break;
}
}
}




У такой реализации есть минусы: 1) много кода 2) при использовании сервисов надо знать имена свойств для доступа к сервисам.
Попытаемся избавиться минуса №2. Для этого воспользуемся интерфейсом IServiceProvider:
public class MyClass : BaseClass, IServiceProvider
{
// если BaseClass наследует Container, то можно использовать виртуальный метод GetService
object IServiceProvider.GetService(Type serviceType)
{
If(serviceType == typeof(ISelectionService))
return this. SelectionService;

if(serviceType == typeof(ILabelEditor))
return this.LabelEditor;

return null;
}

public ISelectionService SelectionService
{

}

public ILabelEditor LabelEditor
{

}
}




Минус №1 стал еще больше, т.е. количество кода увеличилось.
Чтобы от него избавиться воспользуемся ServiceContainer’ом, синтаксисом C# 3.0 и Extension Methods’ами:


using System;
using System.ComponentModel.Design;

public class BaseClass { /*...*/ }
public interface ISelectionService { /*...*/ }
public interface ILabelEditor { /*...*/ }

public class SelectionService : ISelectionService
{
public MyClass Owner { get; internal set; }
}

public class LabelEditor : ILabelEditor
{
public MyClass Owner { get; internal set; }
}

public class MyClass : BaseClass, IServiceProvider
{
public MyClass()
{
// создаем контейнер для сервисов
this.Services = new ServiceContainer();

// добавляем в контейнер типы сервисов; экземпляры сервисы будут созданы один раз по-запросу.
this.Services.Add<ISelectionService>((container, type) => new SelectionService() { Owner = this });
this.Services.Add<ILabelEditor>((container, type) => new LabelEditor() { Owner = this });
}

protected ServiceContainer Services { get; private set; }
object IServiceProvider.GetService(Type serviceType)
{
return this.Services.GetService(serviceType);
}
}

public static class ServiceModelHelpers
{
public static void Add<T>(this IServiceContainer sc, ServiceCreatorCallback cb)
{
sc.AddService(typeof(T), cb);
}

public static T Get<T>(this IServiceProvider sp)
{
return (T)sp.GetService(typeof(T));
}
}

В результате, код MyClass сократился значительно.

1/07/2008

Открытие Consumer Electronics Show (CES)

Consumer Electronics Show (CES) 2008 откроет Билл Гейтс. Прямая трансляция выступления Гейтса начнется 7 января в 17:30 (время московское). Cмотреть трансляцию можно с помощью Silverlight.