##// END OF EJS Templates
fixed unpredictable Safe.Dispose behaviour
cin -
r238:bdfdba6b645b v2
parent child
Show More
@@ -1,248 +1,248
1 1 using System;
2 2 using System.Collections.Generic;
3 3
4 4 namespace Implab.Components {
5 5 /// <summary>
6 6 /// Коллекция сервисов, позволяет регистрировать и получать сервисы.
7 7 /// </summary>
8 8 public class ServiceLocator: Disposable, IServiceLocator, IServiceProvider {
9 9 // запись о сервисе
10 struct ServiceEntry : IDisposable {
10 class 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 if (service != null)
25 25 cleanup(service);
26 26 } else
27 27 Safe.Dispose(service);
28 28 }
29 29
30 30 #endregion
31 31 }
32 32
33 33 // словарь существующих сервисов
34 34 readonly Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>();
35 35
36 36 /// <summary>
37 37 /// Получает объект предоставляющий сервис <typeparamref name="T"/>.
38 38 /// </summary>
39 39 /// <typeparam name="T">Тип запрашиваемого сервиса</typeparam>
40 40 /// <returns>Объект, реализующий сервис</returns>
41 41 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
42 42 public T GetService<T>() {
43 43 object result;
44 44 if (TryGetService(typeof(T), out result))
45 45 return (T)result;
46 46 throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, typeof(T)));
47 47 }
48 48
49 49
50 50 /// <summary>
51 51 /// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис
52 52 /// не возникает исключений.
53 53 /// </summary>
54 54 /// <typeparam name="T">Тип требуемого сервиса.</typeparam>
55 55 /// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param>
56 56 /// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns>
57 57 public bool TryGetService<T>(out T service) {
58 58 object result;
59 59 if (TryGetService(typeof(T), out result)) {
60 60 service = (T)result;
61 61 return true;
62 62 }
63 63 service = default(T);
64 64 return false;
65 65 }
66 66
67 67 /// <summary>
68 68 /// Получает объект предоставляющий сервис <paramref name="serviceType"/>
69 69 /// </summary>
70 70 /// <param name="serviceType">Тип запрашиваемого сервиса</param>
71 71 /// <returns>Объект, реализующий сервис</returns>
72 72 /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception>
73 73 public object GetService(Type serviceType) {
74 74 object result;
75 75 if (TryGetService(serviceType, out result))
76 76 return result;
77 77 throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, serviceType));
78 78 }
79 79
80 80 /// <summary>
81 81 /// Пытается получить требуемый сервис или совместимый с ним.
82 82 /// </summary>
83 83 /// <returns><c>true</c>, если сервис был найден, <c>false</c> в противном случае..</returns>
84 84 /// <param name="serviceType">Тип запрашиваемого сервиса.</param>
85 85 /// <param name="service">Искомый сервис.</param>
86 86 public virtual bool TryGetService(Type serviceType, out object service) {
87 87 Safe.ArgumentNotNull(serviceType, "serviceType");
88 88 AssertNotDisposed();
89 89
90 90 ServiceEntry se;
91 91 if (!m_services.TryGetValue(serviceType, out se)) {
92 92 // ищем ближайщий объект, реализующий нужный сервис
93 93 Type pt = null;
94 94 foreach (var t in m_services.Keys)
95 95 if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt)))
96 96 pt = t;
97 97
98 98 if (pt == null) {
99 99 // нет нужного сервиса
100 100 service = null;
101 101 return false;
102 102 }
103 103
104 104 var pe = m_services[pt];
105 105
106 106 // найденная запись может ссылаться на оригинальную запись с сервисом
107 107 if(pe.origin != null) {
108 108 pt = pe.origin;
109 109 pe = m_services[pt];
110 110 }
111 111
112 112 // добавляем список с обратными ссылками
113 113 if (pe.associated == null)
114 114 pe.associated = new List<Type>();
115 115
116 116 pe.associated.Add(serviceType);
117 117
118 118 // обновляем родительскую запись
119 119 m_services[pt] = pe;
120 120
121 121 // создаем запись со ссылкой
122 122 se = new ServiceEntry {
123 123 service = pe.service,
124 124 origin = pt,
125 125 shared = true // предотвращаем множественные попытки освобождения
126 126 };
127 127
128 128 m_services[serviceType] = se;
129 129 }
130 130
131 131 // запись содержит в себе информацию о сервисе
132 132 if (se.service != null) {
133 133 service = se.service;
134 134 return true;
135 135 }
136 136
137 137 // текущая запись является ссылкой
138 138 if (se.origin != null) {
139 139 se.service = GetService(se.origin);
140 140 m_services[serviceType] = se;
141 141 service = se.service;
142 142 return true;
143 143 }
144 144
145 145 // текущая запись не является ссылкой и не имеет информации о сервисе
146 146 // она должна сожержать информацию об активации
147 147 if (se.activator != null) {
148 148 se.service = se.activator();
149 149
150 150 m_services[serviceType] = se;
151 151
152 152 service = se.service;
153 153 return true;
154 154 }
155 155
156 156 service = null;
157 157 return false;
158 158 }
159 159
160 160 /// <summary>
161 161 /// Регистрирует фабрику для активации сервиса по первому требованию.
162 162 /// </summary>
163 163 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
164 164 /// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param>
165 165 /// <param name = "cleanup">Метод для освобождения экземпляра сервиса, будет вызыван при освобождении сервис-локатора.</param>
166 166 /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks>
167 167 public void Register<T>(Func<T> activator, Action<T> cleanup) {
168 168 Safe.ArgumentNotNull(activator, "activator");
169 169
170 170 AssertNotDisposed();
171 171
172 172 Unregister(typeof(T));
173 173
174 174 var serviceEntry = new ServiceEntry();
175 175 serviceEntry.activator = () => activator();
176 176 if (cleanup != null)
177 177 serviceEntry.cleanup = instance => cleanup((T)instance);
178 178 m_services[typeof(T)] = serviceEntry;
179 179 }
180 180
181 181 public void Register<T>(Func<T> activator) {
182 182 Register(activator, null);
183 183 }
184 184
185 185 /// <summary>
186 186 /// Регистрирует объект, предоставляющий сервис.
187 187 /// </summary>
188 188 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
189 189 /// <param name="service">Объект, предоставляющий сервис.</param>
190 190 /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception>
191 191 /// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks>
192 192 public void Register<T>(T service) {
193 193 Register(service, true);
194 194 }
195 195
196 196 /// <summary>
197 197 /// Регистрирует объект, предоставляющий сервис. Повторная регистрация отменяет уже существующую.
198 198 /// </summary>
199 199 /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam>
200 200 /// <param name="service">Объект, предоставляющий сервис.</param>
201 201 /// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param>
202 202 public void Register<T>(T service, bool shared) {
203 203 Safe.ArgumentNotNull(service, "service");
204 204
205 205 AssertNotDisposed();
206 206
207 207 Unregister(typeof(T));
208 208
209 209 m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared };
210 210 }
211 211
212 212 public void Unregister(Type serviceType) {
213 213 Safe.ArgumentNotNull(serviceType, "serviceType");
214 214
215 215 AssertNotDisposed();
216 216
217 217 ServiceEntry se;
218 218 if (m_services.TryGetValue(serviceType, out se)) {
219 219 if (se.origin != null) {
220 220 var pe = m_services[se.origin];
221 221 pe.associated.Remove(serviceType);
222 222 }
223 223 // освобождаем ресурсы
224 224 se.Dispose();
225 225 m_services.Remove(serviceType);
226 226
227 227 // убираем связанные записи
228 228 if (se.associated != null)
229 229 foreach (var item in se.associated)
230 230 m_services.Remove(item);
231 231 }
232 232 }
233 233
234 234 /// <summary>
235 235 /// Освобождает зарегистрированные сервисы (которые требуется освобоить).
236 236 /// </summary>
237 237 /// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param>
238 238 protected override void Dispose(bool disposing) {
239 239 if (disposing) {
240 240
241 241 foreach (var entry in m_services.Values)
242 242 entry.Dispose();
243 243
244 244 }
245 245 base.Dispose(disposing);
246 246 }
247 247 }
248 248 } No newline at end of file
@@ -1,166 +1,164
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Text.RegularExpressions;
6 6 using System.Diagnostics;
7 7 using System.Collections;
8 8 using System.Runtime.CompilerServices;
9 9
10 10 #if NET_4_5
11 11 using System.Threading.Tasks;
12 12 #endif
13 13
14 14 namespace Implab
15 15 {
16 16 public static class Safe
17 17 {
18 18 [MethodImpl(MethodImplOptions.AggressiveInlining)]
19 19 public static void ArgumentAssert(bool condition, string paramName) {
20 20 if (!condition)
21 21 throw new ArgumentException("The parameter is invalid", paramName);
22 22 }
23 23
24 24 [MethodImpl(MethodImplOptions.AggressiveInlining)]
25 25 public static void ArgumentMatch(string value, string paramName, Regex rx) {
26 26 if (rx == null)
27 27 throw new ArgumentNullException("rx");
28 28 if (!rx.IsMatch(value))
29 29 throw new ArgumentException(String.Format("The prameter value must match {0}", rx), paramName);
30 30 }
31 31
32 32 [MethodImpl(MethodImplOptions.AggressiveInlining)]
33 33 public static void ArgumentNotEmpty(string value, string paramName) {
34 34 if (String.IsNullOrEmpty(value))
35 35 throw new ArgumentException("The parameter can't be empty", paramName);
36 36 }
37 37
38 38 [MethodImpl(MethodImplOptions.AggressiveInlining)]
39 39 public static void ArgumentNotEmpty<T>(T[] value, string paramName) {
40 40 if (value == null || value.Length == 0)
41 41 throw new ArgumentException("The array must be not emty", paramName);
42 42 }
43 43
44 44 [MethodImpl(MethodImplOptions.AggressiveInlining)]
45 45 public static void ArgumentNotNull(object value, string paramName) {
46 46 if (value == null)
47 47 throw new ArgumentNullException(paramName);
48 48 }
49 49
50 50 [MethodImpl(MethodImplOptions.AggressiveInlining)]
51 51 internal static void ArgumentGreaterThan(int value, int min, string paramName) {
52 52 if (value < min)
53 53 throw new ArgumentOutOfRangeException(paramName);
54 54 }
55 55
56 56 [MethodImpl(MethodImplOptions.AggressiveInlining)]
57 57 public static void ArgumentInRange(int value, int min, int max, string paramName) {
58 58 if (value < min || value > max)
59 59 throw new ArgumentOutOfRangeException(paramName);
60 60 }
61 61
62 62 [MethodImpl(MethodImplOptions.AggressiveInlining)]
63 63 public static void ArgumentOfType(object value, Type type, string paramName) {
64 64 if (!type.IsInstanceOfType(value))
65 65 throw new ArgumentException(String.Format("The parameter must be of type {0}", type), paramName);
66 66 }
67 67
68 68 public static void Dispose(params IDisposable[] objects) {
69 69 foreach (var d in objects)
70 70 if (d != null)
71 71 d.Dispose();
72 72 }
73 73
74 74 public static void Dispose(params object[] objects) {
75 75 foreach (var obj in objects) {
76 76 var d = obj as IDisposable;
77 77 if (d != null)
78 78 d.Dispose();
79 79 }
80 80 }
81 81
82 82 public static void DisposeCollection(IEnumerable<IDisposable> objects) {
83 83 foreach (var d in objects)
84 84 Dispose(d);
85 85 }
86 86
87 87 public static void DisposeCollection(IEnumerable objects) {
88 88 foreach (var d in objects)
89 89 Dispose(d);
90 90 }
91 91
92 92 public static void Dispose(object obj) {
93 if (obj is IDisposable) {
93 if (obj is IDisposable)
94 94 Dispose((IDisposable)obj);
95 } else if (obj is IEnumerable) {
96 DisposeCollection((IEnumerable)obj);
97 }
95
98 96 }
99 97
100 98 [DebuggerStepThrough]
101 99 public static void DispatchEvent<T>(this EventHandler<T> handler, object sender, T args) {
102 100 if (handler != null)
103 101 handler(sender, args);
104 102 }
105 103
106 104 [DebuggerStepThrough]
107 105 public static void DispatchEvent(this EventHandler handler, object sender, EventArgs args) {
108 106 if (handler != null)
109 107 handler(sender, args);
110 108 }
111 109
112 110 [DebuggerStepThrough]
113 111 public static IPromise<T> Run<T>(Func<T> action) {
114 112 ArgumentNotNull(action, "action");
115 113
116 114 try {
117 115 return Promise<T>.FromResult(action());
118 116 } catch (Exception err) {
119 117 return Promise<T>.FromException(err);
120 118 }
121 119 }
122 120
123 121 [DebuggerStepThrough]
124 122 public static IPromise Run(Action action) {
125 123 ArgumentNotNull(action, "action");
126 124
127 125 try {
128 126 action();
129 127 return Promise.Success;
130 128 } catch (Exception err) {
131 129 return new FailedPromise(err);
132 130 }
133 131 }
134 132
135 133 [DebuggerStepThrough]
136 134 public static IPromise Run(Func<IPromise> action) {
137 135 ArgumentNotNull(action, "action");
138 136
139 137 try {
140 138 return action() ?? new FailedPromise(new Exception("The action returned null"));
141 139 } catch (Exception err) {
142 140 return new FailedPromise(err);
143 141 }
144 142 }
145 143
146 144 public static void NoWait(IPromise promise) {
147 145 }
148 146
149 147 [DebuggerStepThrough]
150 148 public static IPromise<T> Run<T>(Func<IPromise<T>> action) {
151 149 ArgumentNotNull(action, "action");
152 150
153 151 try {
154 152 return action() ?? Promise<T>.FromException(new Exception("The action returned null"));
155 153 } catch (Exception err) {
156 154 return Promise<T>.FromException(err);
157 155 }
158 156 }
159 157
160 158 #if NET_4_5
161 159 public static void NoWait(Task t) {
162 160 }
163 161 #endif
164 162
165 163 }
166 164 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now