Настройка пространства имен My
Пространство имен My в Visual Basic 2005 облегчает создание впечатляющих приложений. Благодаря API, основанному на задачах, интуитивно-понятной иерархии и инфраструктуре приложений вы можете задействовать всю мощь Microsoft .NET Framework, и зачастую для решения весьма сложной задачи достаточно всего одной строки кода. API пространства имен My построено на полностью расширяемой архитектуре, что позволяет изменять функциональность пространства имен My и добавлять новые сервисы к его иерархии, чтобы адаптировать его под требования конкретного приложения.
Пространство имен My предоставляет свойства и методы, которые «просто работают». При решении распространенных задач программирования его сервисы уменьшают потребность в освоении всей .NET Framework. В отличие от многих типов и методов этой инфраструктуры пространство имен My «заточено» на простые решения частых проблем. Однако, если у вас другие требования, отказываться от него незачем. Применяя модель адаптации My, вы можете изменять поведение его членов так, как это нужно в вашем приложении.
Конфигурирование существующих членов пространства имен My
Одна из серьезнейших проблем, с которыми сталкиваются разработчики при использовании универсальных инфраструктур, — задача конфигурирования. Поэтому члены пространства имен My поддерживают конфигурацию по умолчанию, подходящую в большинстве случаев.
Так, пространство имен System.Security содержит мощную, свободно расширяемую инфраструктуру для построения защиты на основе ролей. Однако обычная задача, скажем, настройка классов, нужных для выполнения от имени текущего пользователя Windows, может вызвать затруднения:
System.Threading.Thread.CurrentPrincipal = _
New System.Security.Principal.WindowsPrincipal( _
System.Security.Principal.WindowsIdentity.GetCurrent)
My.User по умолчанию инкапсулирует текущего пользователя Windows, и инициализирующий код становится не нужен. Но конфигурацию My.User по умолчанию, как и многих другие членов пространства My, при необходимости можно изменить:
My.User.CurrentPrincipal = _
New WindowsPrincipal(WindowsIdentity.GetAnonymous())
Изменяя свойство CurrentPrincipal в My.User, вы меняете пользователя, от имени которого будет выполняться приложение. В свою очередь My.User возвращает информацию о заданном пользователе.
My.User логически инкапсулирует пользователя, который в данный момент вошел в систему. По умолчанию My.User настроен на текущего пользователя Windows. Но выполнение от имени пользователя Windows не всегда уместно: иногда для получения информации о пользователе приходится полагаться на внешнюю инфраструктуру, например на базу данных SQL. Применяя инфраструктуру System.Security.Principal, My.User можно настроить на использование реализаций IPrincipal и IIdentity, предназначенных для взаимодействия с альтернативными моделями аутентификации и авторизации:
My.User.CurrentPrincipal = CustomPrincipal
После того как задано My.User.CurrentPrincipal, при последующих вызовах My.User будет запрашивать вашу реализацию проверки имени пользователя, его ролей и т. д. Разработка собственных реализаций IPrincipal и IIdentity выходит за рамки этой статьи.
Настройка My.Application.Log
Во многих приложениях нужна детальная трассировочная информация для наблюдения за его состоянием и аудита. My.Application.Log содержит простые методы для записи трассировочной информации различных категорий в файл журнала:
Private Sub MyApplication_UnhandledException( _
ByVal sender As Object, ByVal e _
As UnhandledExceptionEventArgs) _
Handles Me.UnhandledException
My.Application.Log.WriteException(e, _
TraceEventType.Critical, "The application has crashed _
due to an unhandled exception")
End Sub
Так как My.Application.Log опирается на инфраструктуру System.Diagnostics.TraceSource, его параметры по умолчанию можно изменять, а также задавать направление и фильтрацию сообщений через файл .config. Например, зачастую требуется записывать критические сообщения об ошибках в Application Event Log, чтобы системные администраторы могли их обнаружить. Для перенаправления всех сообщений в файл (а критических — в Application Event Log) добавьте в файл App.config код, приведенный в листинге 1.
Применяя модель расширения, содержащуюся в пространстве имен My, можно добавлять к нему новые члены.
Листинг 1. Распределение трассировочных сообщений
<system.diagnostics>
<sources>
<!-- DefaultSource используется My.Application.Log
для записи сообщений -->
<source name="DefaultSource" switchName="DefaultSwitch">
<!-- Секция listeners определяет, куда записывать
сообщения. Я задаю файл и журнал событий. -->
<listeners>
<add name="FileLog"/>
<add name="EventLog"/>
</listeners>
</source>
</sources>
<!-- Секция switches определяет, когда TraceSource включен
или выключен. Здесь я указываю, что My.Application.Log
включен для всех сообщений с уровнем значимости
большим или равным уровню Information. -->
<switches>
<add name="DefaultSwitch" value="Information" />
</switches>
<!-- Слушатели записывают сообщения в журнал. -->
<sharedListeners>
<add name="FileLog" type=
"Microsoft.VisualBasic.Logging.FileLogTraceListener"
initializeData="FileLogWriter"/>
<add name="EventLog" type=
"System.Diagnostics.EventLogTraceListener"
initializeData="My Customization Example">
<!-- Добавив фильтр, я настроил слушатель на запись
лишь подмножества сообщений. Здесь в Event Log
добавляются только критические сообщения.-->
<filter type="System.Diagnostics.EventTypeFilter"
initializeData="Critical"/>
</add>
</sharedListeners>
</system.diagnostics>
My.Application.Log управляется файлом .config, и у него есть дополнительное преимущество — его можно изменить после развертывания приложения, так как перекомпиляция не потребуется.
Настройка My.Computer.Network
My.Computer.Network содержит простые методы для определения доступности сети, а также для закачивания и скачивания файлов:
If My.Computer.Network.IsAvailable Then
My.Computer.Network.DownloadFile( _
"http://myserver.com/log.txt", _
"c:\archives\serverlog.txt")
End If
My.Computer.Network опирается на классы System.Net и наследует их конфигурацию периода выполнения. Например, администраторы могут добавить следующий раздел в конфигурационный файл компьютера или приложения и задать прокси-сервер по умолчанию для всех сетевых запросов:
<system.net>
<defaultProxy>
<proxy usesystemdefault="true"/>
</defaultProxy>
</system.net>
В свою очередь, My.Computer.Network автоматически определит эти параметры и применит их.
Настройка My.Settings
My.Settings инкапсулирует мощную инфраструктуру для управления параметрами приложения вроде адресов Web-сервисов и пользовательских настроек. My.Settings позволяет добавлять и удалять параметры на этапе разработки и обращаться к ним в период выполнения без написания утомительного и чреватого ошибками кода для поиска, разбора и обновления конфигурационных файлов. Так как многим приложениям требуется хранение параметров в других местах (например в реестре Windows или в INI-файлах), можно реализовать собственную логику, определяющую, откуда и как My.Settings читает параметры.
В конфигурационной модели My.Settings применен проектировочный шаблон Provider, широко используемый в ASP.NET 2.0 Settings Provider реализует логику чтения и записи параметров в конкретной среде, например в Web-сервисе или реестре Windows. Settings Provider можно указывать для каждого параметра отдельно или сразу для всех в My.Settings.
Модель расширения пространства имен My упрощает и делает более согласованным процесс совместного использования решений разработчиками.
В Visual Studio 2005 провайдеры Settings Provider можно указать в сетке свойств для параметров. Для задания провайдера служит формат Namespace.Type.Assembly. На рис. 1 показан пример настройки My.Settings на использование RegistrySettingsProvider, включенный в исходный код к этой статье.
Рис. 1. Применение RegistrySettingsProvider
Расширение пространства имен My
Пространство имен My обеспечивает решение многих распространенных задач программирования, но при работе в специфических областях могут встречаться проблемы, не решаемые им. Так, My.Computer поддерживает чтение и запись в последовательный порт, но не поддерживает USB-порты; My.Application содержит функциональность трассировки, но не предоставляет средств отчета о крахе; программы, активно работающие с данными, могут пользоваться узлом «My.Data» верхнего уровня для выполнения рутинных операций с данными. Применяя модель расширения, содержащуюся в пространстве имен My, можно добавлять к нему новые члены. Передача расширений пространства имен My другим разработчикам в той же предметной области позволит им использовать расширения так же легко, как и остальное содержимое пространства My.
Возможно, вы недоумеваете, а зачем его вообще расширять. В наши дни разработчики используют внешние библиотеки классов для решения конкретных проблем, так зачем писать код, если кто-то уже сделал это? Зачастую разработчики используют сторонние (или даже общедоступные) библиотеки классов, но они требуют определенных усилий в изучении. Какие проблемы решает библиотека? Как ее нужно использовать? И как ее использую я? У вас непременно появятся вопросы такого рода. Существование различных стандартов разработки усложняет процесс обучения. Модель расширения пространства имен My упрощает и делает более согласованным процесс совместного использования решений разработчиками. Следуя ее четким правилам, можно предлагать решения, гарантирующие, что разработчик поймет, как их следует использовать, и что разработчики любой квалификации смогут эффективно применять их в своей деятельности.
В отличие от других пространств имен .NET Framework, основанных на внешних сборках (DLL), пространство My находится в корневом пространстве имен проекта Visual Basic и размещается в файле в памяти. В листинге 2 показана часть его содержимого.
Листинг 2. Обзор пространства имен My
WindowsApplication1
Namespace My
Module MyProject
Public ReadOnly Property Application() As MyApplication
Public ReadOnly Property Computer() As MyComputer
Public ReadOnly Property User() As User
...
End Module
Partial Class MyApplication
Inherits ApplicationServices.WindowsFormsApplicationBase
End Class
Partial Class My Computer
Inherits Devices.Computer
End Class
End Namespace
Microsoft.VisualBasic.dll
Namespace Devices
Public Class Computer
Public ReadOnly Property Ports() As Ports
...
End Class
End Namespace
Namespace ApplicationServices
Public Class WindowsFormsApplicationBase
Public ReadOnly Property Info() As AssemblyInfo
...
End Class
End Namespace
Свойства Application, Computer и User определены как My.Application, My.Computer и My.User соответственно. Что интересно, My.Application и My.Computer на самом деле возвращают экземпляры классов MyApplication и MyComputer, тоже определенных в пространстве My. Эти классы и тот факт, что они объявлены с ключевым словом Partial, играют важнейшую роль в расширении My.Application и My.Computer.
Добавление членов
Так как My — это лишь пространство имен, добавить свойства верхнего уровня относительно легко. Создайте в проекте новый модуль и поместите его в пространство имен My. Затем пометьте этот модуль атрибутом HideModuleName. Последний указывает IntelliSense скрыть имя модуля от пользователей, просматривающих пространство My.
Наконец, добавьте к модулю необходимые свойства. Для каждого такого свойства добавьте сопутствующее поле типа ThreadSafeObjectProvider(Of T). Оно применяется для создания безопасных в многопоточной среде экземпляров внутри свойства, т. е. каждый поток, обращающийся к расширенному свойству, получает свой экземпляр возвращенного типа. В следующем примере к пространству имен My добавляется расширение SampleExtension:
Namespace My
<HideModuleName()> _
Module MyProject
Private m_extension As _
New ThreadSafeObjectProvider(Of SampleExtension)
Public ReadOnly Property SampleExtension() _
As SampleExtension
Get
Return m_extension.GetInstance()
End Get
End Property
End Module
End Namespace
Так как типы, возвращаемые My.Application и My.Computer, определены в проекте как Partial, их расширение — задача, решаемая достаточно прямолинейно. Сначала добавьте дружественный класс MyComputer, для которого укажите ключевое слово Partial. Поместите новый класс MyComputer в пространство имен My и добавьте к нему свойства и методы. Добавляемые свойства и методы сразу же становятся доступны из My.Computer:
Imports System.Net.NetworkInformation
Namespace My
Partial Class MyComputer
Public ReadOnly Property IPAddresses() _
As IPAddressCollection
...
End Property
End Class
End Namespace
Расширение My.Application аналогично. Единственное отличие - имя Partial-класса (MyApplication).
Расширение модели событий My.Application
My.Application в проектах Windows Forms поддерживает набор событий, инкапсулирующих изменения в общем состоянии приложения, например его запуск и закрытие, а также изменения в соединении с сетью и сбои в приложении. Базовый класс Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase, наследником которого является My.Application, содержит входные точки для расширения; их можно использовать для выполнения своих действий до и после срабатывания событий. Вот как вызываются эти методы.
- OnInitialize. Первая вызываемая входная точка. Здесь можно обработать аргументы командной строки. Если OnInitialize возвращает False, последовательность событий прерывается и приложение завершается.
- OnCreateSplashScreen. Вызывается по окончании инициализации. Здесь задается экран-заставка (splash screen), показываемая во время загрузки приложения.
- OnStartup. Вызывается непосредственно перед событием Startup. Если вернуть из этого метода False, последовательность событий прервется и приложение завершится.
- OnRun. Вызывается после обработки события Startup и до отображения главной формы.
- OnCreateMainForm. Последние из событий запуска приложения. Здесь задается My.Application-свойство MainForm.
- OnShutdown. Вызывается непосредственно перед событием Shutdown.
В листинге 3 показано, как расширить модель событий My.Application, добавив функциональность для вывода экрана-заставки на время, заданное аргументом командной строки.
Листинг 3. Расширение модели событий MyApplication
Class MyApplication
Private m_splashTimeout As Integer = 2000
Private m_timerThread As Thread
''' <summary>
''' Извлечь время отображения экрана-заставки
''' из аргументов командной строки
''' </summary>
Protected Overrides Function OnInitialize( _
ByVal commandLineArgs As ReadOnlyCollection(Of String)) _
As Boolean
For Each argument As String In commandLineArgs
If (argument.Contains("/splashtimeout")) Then
m_splashTimeout = Integer.Parse( _
argument.Split(":")(1)) * 1000
End If
m_timerThread = New Thread(AddressOf RunTimer)
m_timerThread.IsBackground = True
m_timerThread.Start()
Next
Return MyBase.OnInitialize(commandLineArgs)
End Function
''' <summary>
''' До отображения главной формы убедиться, что
''' экран-заставка появилась на требуемое время
''' </summary>
''' <remarks></remarks >
Protected Overrides Sub OnRun()
If SplashScreen IsNot Nothing AndAlso _
SplashScreen.Visible = True AndAlso _
m_timerThread IsNot Nothing AndAlso _
m_timerThread.IsAlive Then
m_timerThread.Join(m_splashTimeout)
End If
MyBase.OnRun()
End Sub
''' <summary>
''' Этот метод выполняется примерно
''' m_splashTimeout миллисекунд
''' </summary>
Private Sub RunTimer()
Dim runningTime As Integer = 0
While runningTime < m_splashTimeout
Thread.Sleep(100)
runningTime += 100
End While
End Sub
End Class
Члены верхнего уровня можно не только добавлять к пространству My, но и удалять или переопределять. Скажем, если в конкретном домене приложения требуется объект приложения типа, отличного от исходного Microsoft.VisualBasic.ApplicationServices.ApplicationBase, то My.Application можно переопределить так, чтобы тот возвращал более подходящий объект приложения. Вы также можете удалить, например, член My.WebServices из пространства имен My, если Web-сервисы вам не нужны.
MyType
Конструкция MyType сообщает компилятору Visual Basic, какие члены верхнего уровня должны быть видимы в проекте данного типа. Так, My.Forms не слишком полезен для проекта Windows Service, поэтому он исключается из пространства имен My. При расширении My конструкция MyType позволяет изменить это пространство имен так, чтобы его пользователи получили полностью адаптированное решение.
MyType — готовое определение, применяемое для условной компиляции членов My. В качестве примера рассмотрим код, используемый для формирования пространства имен My в проектах Windows Forms:
#If _MyType = "WindowsForms" Then
' My.Forms будет включен в пространство имен My
#Const _MYFORMS = True
' My.Application будет содержать специфические
' для Windows Forms события и члены
#Const _MYAPPLICATION = "WindowsForms"
' My.Computer будет содержать члены для Windows-клиентов
#Const _MYCOMPUTER = "Windows"
' My.User будет возвращать пользователя Windows
#Const _MYUSER = "Windows"
#ElseIf _MyType = "Console" Then
' ...
#End If
Когда компилятору Visual Basic передается нестандартный MyType, пространство имен My становится пустым, и вам нужно добавлять обратно требуемые члены. Добавление новых членов верхнего уровня к пространству имен My уже было описано в соответствующем разделе, а для возвращения предопределенных членов вроде My.Forms следует пользоваться константами компилятора.
С каждым предопределенным членом верхнего уровня пространства имен My связана константа компилятора. В частности, булева константа компилятора _MYFORMS определяет, должен ли быть включен «Forms». Все предопределенные члены верхнего уровня пространства имен My и их константы перечислены в табл. 1.
Табл. 1. Константы компиляции пространства имен My
Имя члена |
Константа компиляции |
Возможные значения |
Возвращает |
My.Application | _MYAPPLICATIONTYPE |
| Windows | ApplicationServices.ApplicationBase |
Console | ApplicationServices.ConsoleApplicationBase |
WindowsForms | ApplicationServices.WindowsFormsApplicationBase |
My.Computer | _MYCOMPUTERTYPE |
| Windows | Devices.Computer |
Web | Devices.ServerComputer |
My.Forms | _MYFORMS |
| True | My.Forms включается |
False | My.Forms исключается |
My.User | _MYUSERTYPE |
| Windows | ApplicationServices.User |
Web | ApplicationServices.WebUser |
My.WebServicesr | _MYWEBSERVICES |
| True | My.WebServices включается |
False | My.WebServices исключается |
Так как предопределенные члены верхнего уровня настраиваются через константы компилятора, эти значения нужно передать компилятору в аргументах командной строки. В Visual Studio константы компилятора задаются на странице Compile в Project Designer.
Переопределение My.User
Я продемонстрирую определение собственного MyType на примере My.User, возвращающего нестандартный тип ExtendedUser. Последний содержит вспомогательные методы Login и Logout для выполнения аутентификации. Начните с создания библиотеки классов и добавления к ней следующего класса:
Imports System.Security
Public Class ExtendedUser
Inherits Microsoft.VisualBasic.ApplicationServices.User
Public Function Login(ByVal username As String, _
ByVal password As SecureString) As Boolean
'TODO: выполнить логику входа пользователя
End Function
Public Sub Logout()
'TODO: выполнить логику выхода пользователя
End Sub
End Class
Затем создайте проект Windows Forms и добавьте к нему ссылку на эту библиотеку классов. Добавьте такое определение для My.User:
#If _MyType = "Custom" Then
Namespace My
<HideModuleName()> _
Module MyExtensionModule
Private m_userInstanceProvider As New _
ThreadSafeObjectProvider(Of ExtendedUser)
Public ReadOnly Property User() As ExtendedUser
Get
Return m_userInstanceProvider.GetInstance()
End Get
End Property
End Module
End Namespace
#End If
Присвойте _MyType значение «Custom» и включите предопределенные члены верхнего уровня пространства имен My, передав компилятору соответствующие аргументы. Ниже приведены аргументы для включения всех предопределенных членов, кроме только что определенного My.User:
MyType="Custom", _MYAPPLICATIONTYPE="WindowsForms",
_MyType
_MYCOMPUTERTYPE="Windows", _MYFORMS=True, MYWEBSERVICES=True
Теперь My.User будет возвращать ExtendedUser во всех проектах Windows Forms.
Настройка пространства имен My
При выборе стратегий разработки, развертывания и поддержки расширений пространства имен My, проще всего разделить задачу на две части: создание расширений пространства имен My и создание библиотеки классов. Расширение пространства имен My — это код, предназначенный для раскрытия вашей библиотеки классов в My, как показано выше, а библиотека классов — сборка (DLL), содержащая типы, раскрываемые в расширении.
Когда компилятору Visual Basic передается нестандартный MyType, пространство имен My становится пустым, и вам нужно добавлять обратно требуемые члены.
Рекомендации по разработке
Следующие рекомендации помогут свести к минимуму расходы на поддержку компонентов-расширений пространства имен My.
Включайте только логику расширения. Логика, включаемая в собственно расширение пространства имен My, должна содержать лишь «начинку», необходимую для раскрытия функциональности в этом пространстве имен. Так как исходный код этого компонента включается в проект, обновление расширения весьма дорогостоящая процедура, которой по возможности следует избегать.
Старайтесь не делать каких-либо предположений о проектах. В общем случае, не стоит делать каких-либо предположений о свойствах проекта, в частности о наборе ссылок, директивах Imports или специфических параметрах компилятора (например выключенном параметре Strict). Вместо этого стремитесь свести к минимуму зависимости, используйте полные ссылки на все типы с помощью ключевого слова Global и убедитесь, что расширение компилируется с параметром Strict для выявления ошибок в этом расширении.
Изолируйте код расширения. Чтобы облегчить изоляцию расширений пространства имен My, поместите весь его код в отдельный файл или папку в проекте.
Разработка библиотек классов для My
Как и в большинстве объектных моделей, некоторые проектировочные шаблоны работают в пространстве имен My, а некоторые — нет. Разрабатывая расширения My, придерживайтесь следующих принципов.
API без состояний. Методы пространства имен My должны предоставлять конечное решение конкретной задачи. Входные параметры должны принимать лишь данные, необходимые для выполнения задачи. Методы, которые полагаются на предыдущее состояние, в My обычно неуместны. В My.Computer.FileSystem есть хорошие примеры API без состояний, отлично работающих в контексте My.
Глобальные экземпляры. Единственное состояние, поддерживаемое в My, — глобальное для всего проекта. Так, My.Application.Info инкапсулирует состояние, общее для всего приложения.
Простые типы параметров. Самым простым методам, как правило, не нужны параметры сложных типов. Они должны принимать строки, элементарные типы и т. д.
Методы-фабрики. Экземпляры некоторых типов создать не так-то просто. Методы-фабрики в расширениях My облегчают применение таких типов. Пример хорошо работающего метода-фабрики — My.Computer.FileSystem.OpenTextFileReader. Так как существует несколько типов Stream, этот метод четко указывает, какой Stream нужно использовать и каким образом.
В этих рекомендациях нет общих правил разработки библиотек классов. Это рекомендации, предназначенные разработчикам, использующим Visual Basic и пространство имен My.
Упаковка и развертывание расширений
Процесс упаковки и развертывания расширений My аналогичен развертыванию стандартной библиотеки классов или элемента управления от сторонних поставщиков. Нужно лишь обеспечить простой механизм добавления расширений к каждому проекту. Visual Studio 2005 поддерживает улучшенный формат шаблонов, хорошо подходящий для упаковки и распространения расширений пространства My.
Файл шаблона Visual Studio (.vstemplate) инкапсулирует контент, который можно либо использовать как основу для новых проектов (шаблон проекта), либо добавить к существующему проекту (шаблон элемента). Упаковка расширений My в виде файлов шаблона позволяет бесшовно интегрировать их с Visual Studio и дает возможность пользователям добавлять расширения к проекту через диалоговые окна Add New Item и New Project. Преимущества этого подхода в том, что шаблоны Visual Studio можно устанавливать на уровне пользователя или на уровне компьютера. Пример расширения пространства имен My, упакованного в виде шаблона элемента Visual Studio, содержится в исходном коде, который можно скачать для этой статьи.
Заключение
Пространство имен My предоставляет полнофункциональный набор членов и сервисов, упрощающих решение многих задач программирования. Однако, хотя этот набор весьма обширен, он служит не более чем отправной точкой для дальнейших усовершенствований. Его модель расширения позволяет создавать, разделять и улучшать пространство имен My по мере изменения требований к приложению.
|