ServiceLocator.cs
247 lines
| 11.6 KiB
| text/x-csharp
|
CSharpLexer
/ Implab / ServiceLocator.cs
cin
|
r40 | using System; | |
using System.Collections.Generic; | |||
namespace Implab { | |||
/// <summary> | |||
/// Коллекция сервисов, позволяет регистрировать и получать сервисы. | |||
/// </summary> | |||
public class ServiceLocator: Component, IServiceLocator, IServiceProvider { | |||
cin
|
r86 | // запись о сервисе | |
struct ServiceEntry : IDisposable { | |||
cin
|
r40 | public object service; // сервис | |
public bool shared; // признак того, что сервис НЕ нужно освобождать | |||
public Func<object> activator; // активатор сервиса при первом обращении | |||
cin
|
r86 | public Action<object> cleanup; // функция для очистки сервиса | |
cin
|
r40 | public List<Type> associated; // ссылки на текущую запись | |
public Type origin; // ссылка на оригинальную запись о сервисе | |||
cin
|
r86 | ||
#region IDisposable implementation | |||
public void Dispose() { | |||
if (shared) | |||
return; | |||
cin
|
r88 | if (cleanup != null) { | |
if (service != null) | |||
cleanup(service); | |||
} else | |||
Safe.Dispose(service); | |||
cin
|
r86 | } | |
#endregion | |||
cin
|
r40 | } | |
// словарь существующих сервисов | |||
cin
|
r69 | readonly Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>(); | |
cin
|
r40 | ||
/// <summary> | |||
/// Получает объект предоставляющий сервис <typeparamref name="T"/>. | |||
/// </summary> | |||
/// <typeparam name="T">Тип запрашиваемого сервиса</typeparam> | |||
/// <returns>Объект, реализующий сервис</returns> | |||
/// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> | |||
public T GetService<T>() { | |||
cin
|
r69 | object result; | |
if (TryGetService(typeof(T), out result)) | |||
return (T)result; | |||
throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, typeof(T))); | |||
cin
|
r40 | } | |
/// <summary> | |||
/// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис | |||
/// не возникает исключений. | |||
/// </summary> | |||
/// <typeparam name="T">Тип требуемого сервиса.</typeparam> | |||
/// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param> | |||
/// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns> | |||
public bool TryGetService<T>(out T service) { | |||
cin
|
r69 | object result; | |
if (TryGetService(typeof(T), out result)) { | |||
service = (T)result; | |||
return true; | |||
} | |||
service = default(T); | |||
return false; | |||
cin
|
r40 | } | |
/// <summary> | |||
/// Получает объект предоставляющий сервис <paramref name="serviceType"/> | |||
/// </summary> | |||
/// <param name="serviceType">Тип запрашиваемого сервиса</param> | |||
/// <returns>Объект, реализующий сервис</returns> | |||
/// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> | |||
cin
|
r68 | public object GetService(Type serviceType) { | |
cin
|
r69 | object result; | |
if (TryGetService(serviceType, out result)) | |||
return result; | |||
throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, serviceType)); | |||
cin
|
r68 | } | |
cin
|
r69 | /// <summary> | |
/// Пытается получить требуемый сервис или совместимый с ним. | |||
/// </summary> | |||
/// <returns><c>true</c>, если сервис был найден, <c>false</c> в противном случае..</returns> | |||
/// <param name="serviceType">Тип запрашиваемого сервиса.</param> | |||
/// <param name="service">Искомый сервис.</param> | |||
public virtual bool TryGetService(Type serviceType, out object service) { | |||
cin
|
r86 | Safe.ArgumentNotNull(serviceType, "serviceType"); | |
cin
|
r40 | AssertNotDisposed(); | |
ServiceEntry se; | |||
if (!m_services.TryGetValue(serviceType, out se)) { | |||
// ищем ближайщий объект, реализующий нужный сервис | |||
Type pt = null; | |||
foreach (var t in m_services.Keys) | |||
if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt))) | |||
pt = t; | |||
cin
|
r69 | ||
if (pt == null) { | |||
// нет нужного сервиса | |||
service = null; | |||
return false; | |||
} | |||
cin
|
r40 | ||
var pe = m_services[pt]; | |||
// найденная запись может ссылаться на оригинальную запись с сервисом | |||
if(pe.origin != null) { | |||
pt = pe.origin; | |||
pe = m_services[pt]; | |||
} | |||
// добавляем список с обратными ссылками | |||
if (pe.associated == null) | |||
pe.associated = new List<Type>(); | |||
pe.associated.Add(serviceType); | |||
// обновляем родительскую запись | |||
m_services[pt] = pe; | |||
// создаем запись со ссылкой | |||
se = new ServiceEntry { | |||
service = pe.service, | |||
origin = pt, | |||
shared = true // предотвращаем множественные попытки освобождения | |||
}; | |||
m_services[serviceType] = se; | |||
} | |||
// запись содержит в себе информацию о сервисе | |||
cin
|
r69 | if (se.service != null) { | |
service = se.service; | |||
return true; | |||
} | |||
cin
|
r40 | ||
// текущая запись является ссылкой | |||
if (se.origin != null) { | |||
se.service = GetService(se.origin); | |||
m_services[serviceType] = se; | |||
cin
|
r69 | service = se.service; | |
return true; | |||
cin
|
r40 | } | |
// текущая запись не является ссылкой и не имеет информации о сервисе | |||
// она должна сожержать информацию об активации | |||
if (se.activator != null) { | |||
se.service = se.activator(); | |||
m_services[serviceType] = se; | |||
cin
|
r69 | service = se.service; | |
return true; | |||
cin
|
r40 | } | |
cin
|
r69 | service = null; | |
return false; | |||
cin
|
r40 | } | |
/// <summary> | |||
/// Регистрирует фабрику для активации сервиса по первому требованию. | |||
/// </summary> | |||
/// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> | |||
/// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param> | |||
cin
|
r86 | /// <param name = "cleanup">Метод для освобождения экземпляра сервиса, будет вызыван при освобождении сервис-локатора.</param> | |
cin
|
r40 | /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks> | |
cin
|
r86 | public void Register<T>(Func<T> activator, Action<T> cleanup) { | |
Safe.ArgumentNotNull(activator, "activator"); | |||
cin
|
r40 | ||
AssertNotDisposed(); | |||
Unregister(typeof(T)); | |||
cin
|
r88 | var serviceEntry = new ServiceEntry(); | |
serviceEntry.activator = () => activator(); | |||
if (cleanup != null) | |||
serviceEntry.cleanup = instance => cleanup((T)instance); | |||
m_services[typeof(T)] = serviceEntry; | |||
cin
|
r40 | } | |
cin
|
r86 | public void Register<T>(Func<T> activator) { | |
Register(activator, null); | |||
} | |||
cin
|
r40 | /// <summary> | |
/// Регистрирует объект, предоставляющий сервис. | |||
/// </summary> | |||
/// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> | |||
/// <param name="service">Объект, предоставляющий сервис.</param> | |||
/// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception> | |||
/// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks> | |||
public void Register<T>(T service) { | |||
Register(service, true); | |||
} | |||
/// <summary> | |||
cin
|
r87 | /// Регистрирует объект, предоставляющий сервис. Повторная регистрация отменяет уже существующую. | |
cin
|
r40 | /// </summary> | |
/// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> | |||
/// <param name="service">Объект, предоставляющий сервис.</param> | |||
/// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param> | |||
public void Register<T>(T service, bool shared) { | |||
cin
|
r86 | Safe.ArgumentNotNull(service, "service"); | |
cin
|
r40 | ||
AssertNotDisposed(); | |||
Unregister(typeof(T)); | |||
m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared }; | |||
} | |||
public void Unregister(Type serviceType) { | |||
cin
|
r86 | Safe.ArgumentNotNull(serviceType, "serviceType"); | |
cin
|
r40 | ||
AssertNotDisposed(); | |||
ServiceEntry se; | |||
if (m_services.TryGetValue(serviceType, out se)) { | |||
cin
|
r87 | if (se.origin != null) { | |
var pe = m_services[se.origin]; | |||
pe.associated.Remove(serviceType); | |||
} | |||
cin
|
r40 | // освобождаем ресурсы | |
cin
|
r87 | se.Dispose(); | |
cin
|
r40 | m_services.Remove(serviceType); | |
// убираем связанные записи | |||
if (se.associated != null) | |||
foreach (var item in se.associated) | |||
m_services.Remove(item); | |||
} | |||
} | |||
/// <summary> | |||
/// Освобождает зарегистрированные сервисы (которые требуется освобоить). | |||
/// </summary> | |||
/// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param> | |||
protected override void Dispose(bool disposing) { | |||
if (disposing) { | |||
foreach (var entry in m_services.Values) | |||
cin
|
r86 | entry.Dispose(); | |
cin
|
r40 | ||
} | |||
base.Dispose(disposing); | |||
} | |||
} | |||
} |