##// END OF EJS Templates
minor fixes in the service locator class
cin -
r87:79badb3ed195 v2
parent child
Show More
@@ -1,248 +1,249
1 1 using System;
2 2 using System.Collections.Generic;
3 3
4 4 namespace Implab {
5 5 /// <summary>
6 6 /// Коллекция сервисов, позволяет регистрировать и получать сервисы.
7 7 /// </summary>
8 8 public class ServiceLocator: Component, IServiceLocator, IServiceProvider {
9 9 // запись о сервисе
10 10 struct ServiceEntry : IDisposable {
11 11 public object service; // сервис
12 12 public bool shared; // признак того, что сервис НЕ нужно освобождать
13 13 public Func<object> activator; // активатор сервиса при первом обращении
14 14 public Action<object> cleanup; // функция для очистки сервиса
15 15 public List<Type> associated; // ссылки на текущую запись
16 16 public Type origin; // ссылка на оригинальную запись о сервисе
17 17
18 18 #region IDisposable implementation
19 19
20 20 public void Dispose() {
21 21 if (shared)
22 22 return;
23 23 if (cleanup != null)
24 24 cleanup(service);
25 25 else {
26 26 var d = service as IDisposable;
27 27 if (d != null)
28 28 d.Dispose();
29 29 }
30 30 }
31 31
32 32 #endregion
33 33 }
34 34
35 35 // словарь существующих сервисов
36 36 readonly Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>();
37 37
38 38 /// <summary>
39 39 /// Получает объект предоставляющий сервис <typeparamref name="T"/>.
40 40 /// </summary>
41 41 /// <typeparam name="T">Тип запрашиваемого сервиса</typeparam>
42 42 /// <returns>Объект, реализующий сервис</returns>
43 43 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
44 44 public T GetService<T>() {
45 45 object result;
46 46 if (TryGetService(typeof(T), out result))
47 47 return (T)result;
48 48 throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, typeof(T)));
49 49 }
50 50
51 51
52 52 /// <summary>
53 53 /// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис
54 54 /// не возникает исключений.
55 55 /// </summary>
56 56 /// <typeparam name="T">Тип требуемого сервиса.</typeparam>
57 57 /// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param>
58 58 /// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns>
59 59 public bool TryGetService<T>(out T service) {
60 60 object result;
61 61 if (TryGetService(typeof(T), out result)) {
62 62 service = (T)result;
63 63 return true;
64 64 }
65 65 service = default(T);
66 66 return false;
67 67 }
68 68
69 69 /// <summary>
70 70 /// Получает объект предоставляющий сервис <paramref name="serviceType"/>
71 71 /// </summary>
72 72 /// <param name="serviceType">Тип запрашиваемого сервиса</param>
73 73 /// <returns>Объект, реализующий сервис</returns>
74 74 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
75 75 public object GetService(Type serviceType) {
76 76 object result;
77 77 if (TryGetService(serviceType, out result))
78 78 return result;
79 79 throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, serviceType));
80 80 }
81 81
82 82 /// <summary>
83 83 /// Пытается получить требуемый сервис или совместимый с ним.
84 84 /// </summary>
85 85 /// <returns><c>true</c>, если сервис был найден, <c>false</c> в противном случае..</returns>
86 86 /// <param name="serviceType">Тип запрашиваемого сервиса.</param>
87 87 /// <param name="service">Искомый сервис.</param>
88 88 public virtual bool TryGetService(Type serviceType, out object service) {
89 89 Safe.ArgumentNotNull(serviceType, "serviceType");
90 90 AssertNotDisposed();
91 91
92 92 ServiceEntry se;
93 93 if (!m_services.TryGetValue(serviceType, out se)) {
94 94 // ищем ближайщий объект, реализующий нужный сервис
95 95 Type pt = null;
96 96 foreach (var t in m_services.Keys)
97 97 if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt)))
98 98 pt = t;
99 99
100 100 if (pt == null) {
101 101 // нет нужного сервиса
102 102 service = null;
103 103 return false;
104 104 }
105 105
106 106 var pe = m_services[pt];
107 107
108 108 // найденная запись может ссылаться на оригинальную запись с сервисом
109 109 if(pe.origin != null) {
110 110 pt = pe.origin;
111 111 pe = m_services[pt];
112 112 }
113 113
114 114 // добавляем список с обратными ссылками
115 115 if (pe.associated == null)
116 116 pe.associated = new List<Type>();
117 117
118 118 pe.associated.Add(serviceType);
119 119
120 120 // обновляем родительскую запись
121 121 m_services[pt] = pe;
122 122
123 123 // создаем запись со ссылкой
124 124 se = new ServiceEntry {
125 125 service = pe.service,
126 126 origin = pt,
127 127 shared = true // предотвращаем множественные попытки освобождения
128 128 };
129 129
130 130 m_services[serviceType] = se;
131 131 }
132 132
133 133 // запись содержит в себе информацию о сервисе
134 134 if (se.service != null) {
135 135 service = se.service;
136 136 return true;
137 137 }
138 138
139 139 // текущая запись является ссылкой
140 140 if (se.origin != null) {
141 141 se.service = GetService(se.origin);
142 142 m_services[serviceType] = se;
143 143 service = se.service;
144 144 return true;
145 145 }
146 146
147 147 // текущая запись не является ссылкой и не имеет информации о сервисе
148 148 // она должна сожержать информацию об активации
149 149 if (se.activator != null) {
150 150 se.service = se.activator();
151 151
152 152 m_services[serviceType] = se;
153 153
154 154 service = se.service;
155 155 return true;
156 156 }
157 157
158 158 service = null;
159 159 return false;
160 160 }
161 161
162 162 /// <summary>
163 163 /// Регистрирует фабрику для активации сервиса по первому требованию.
164 164 /// </summary>
165 165 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
166 166 /// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param>
167 167 /// <param name = "cleanup">Метод для освобождения экземпляра сервиса, будет вызыван при освобождении сервис-локатора.</param>
168 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
169 168 /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks>
170 169 public void Register<T>(Func<T> activator, Action<T> cleanup) {
171 170 Safe.ArgumentNotNull(activator, "activator");
172 171
173 172 AssertNotDisposed();
174 173
175 174 Unregister(typeof(T));
176 175
177 176 m_services[typeof(T)] = new ServiceEntry {
178 177 activator = () => activator(),
179 178 cleanup = instance => cleanup((T)instance)
180 179 };
181 180 }
182 181
183 182 public void Register<T>(Func<T> activator) {
184 183 Register(activator, null);
185 184 }
186 185
187 186 /// <summary>
188 187 /// Регистрирует объект, предоставляющий сервис.
189 188 /// </summary>
190 189 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
191 190 /// <param name="service">Объект, предоставляющий сервис.</param>
192 191 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
193 192 /// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks>
194 193 public void Register<T>(T service) {
195 194 Register(service, true);
196 195 }
197 196
198 197 /// <summary>
199 /// Регистрирует объект, предоставляющий сервис.
198 /// Регистрирует объект, предоставляющий сервис. Повторная регистрация отменяет уже существующую.
200 199 /// </summary>
201 200 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
202 201 /// <param name="service">Объект, предоставляющий сервис.</param>
203 202 /// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param>
204 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
205 203 public void Register<T>(T service, bool shared) {
206 204 Safe.ArgumentNotNull(service, "service");
207 205
208 206 AssertNotDisposed();
209 207
210 208 Unregister(typeof(T));
211 209
212 210 m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared };
213 211 }
214 212
215 213 public void Unregister(Type serviceType) {
216 214 Safe.ArgumentNotNull(serviceType, "serviceType");
217 215
218 216 AssertNotDisposed();
219 217
220 218 ServiceEntry se;
221 219 if (m_services.TryGetValue(serviceType, out se)) {
220 if (se.origin != null) {
221 var pe = m_services[se.origin];
222 pe.associated.Remove(serviceType);
223 }
222 224 // освобождаем ресурсы
223 if (se.service != null && !se.shared)
224 ((IDisposable)se.service).Dispose();
225 se.Dispose();
225 226 m_services.Remove(serviceType);
226 227
227 228 // убираем связанные записи
228 229 if (se.associated != null)
229 230 foreach (var item in se.associated)
230 231 m_services.Remove(item);
231 232 }
232 233 }
233 234
234 235 /// <summary>
235 236 /// Освобождает зарегистрированные сервисы (которые требуется освобоить).
236 237 /// </summary>
237 238 /// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param>
238 239 protected override void Dispose(bool disposing) {
239 240 if (disposing) {
240 241
241 242 foreach (var entry in m_services.Values)
242 243 entry.Dispose();
243 244
244 245 }
245 246 base.Dispose(disposing);
246 247 }
247 248 }
248 249 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now