Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
course_(Windows&Web).docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
4.68 Mб
Скачать

Реализация iNotifyPropertyChanged средствами аспектно-ориентированного программирования

Как было сказано ранее, так как создание экземпляров моделей представления происходит посредством IoC контейнера, возможно расширить логику создания объекта и дополнять его произвольными аспектами.

Данная функциональность недоступна в базовой версии MEF, и требует использования дополнительных библиотек, MefContrib и DynamicProxy (которые помимо перехвата создания объекта реализует некоторые другие полезные функции, выходящие за рамки лекции).

В целях лучшей расширяемости приложения следует объявить перечисление со всеми доступными аспектами (на текущий момент с 1 аспектом):

1: [Flags]

2: public enum Aspects

3: {

4: NotifyPropertyChanged = 1,

5: }

А также необходимо написать небольшой метод-расширение, добавляющий к контейнеру MEF логику перехвата создания объектов. Если в метаданных создаваемого экземпляра найден атрибут AspectMetadata и указан аспект NotifyPropertyChanged, он будет обернут прокси-объектом, реализующим логику INotifyPropertyChanged:

1: public static class AopExtensions

2: {

3: public const string AspectMetadata = "AspectMetadata";

4:

5: public static InterceptionConfiguration AddAopInterception(

6: this InterceptionConfiguration interceptionConfiguration)

7: {

8: return interceptionConfiguration

9: .AddInterceptionCriteria(

10: new PredicateInterceptionCriteria(

11: new PropertyChangedDynamicProxyExportInterceptor(),

12: def => def.ExportDefinitions.Any(export =>

13: export.Metadata.ContainsKey(AspectMetadata) &&

14: ((Aspects)export.Metadata[AspectMetadata] &

15: Aspects.NotifyPropertyChanged) != 0)));

16: }

17: }

1: internal class PropertyChangedDynamicProxyExportInterceptor :

2: IExportedValueInterceptor

3: {

4: private static readonly ProxyGenerator Generator =

5: new ProxyGenerator();

6: private readonly Type[] _additionalInterfaces = new[]

7: { typeof(INotifyPropertyChanged) };

8:

9: public object Intercept(object value)

10: {

11: object proxy = Generator.CreateClassProxy(

12: value.GetType(), _additionalInterfaces,

13: new[] { new PropertyChangedInterceptor() });

14:

15: Type currentType = value.GetType();

16:

17: do

18: {

19: FieldInfo[] fields =

20: currentType.GetFields(BindingFlags.Instance |

21: BindingFlags.NonPublic | BindingFlags.Public)

22: .Where(field =>

23: (field.Attributes & FieldAttributes.InitOnly) == 0)

24: .ToArray();

25:

26: fields.Select(field => new { Field = field,

27: Value = field.GetValue(value) })

28: .ForEach(desc =>

29: desc.Field.SetValue(proxy, desc.Value));

30:

31: currentType = currentType.BaseType;

32: } while (currentType != null);

33:

34: return proxy;

35: }

36: }

1: internal class PropertyChangedInterceptor : IInterceptor

2: {

3: private event PropertyChangedEventHandler PropertyChanged;

4:

5: public void Intercept(IInvocation invocation)

6: {

7: string methodName = invocation.Method.Name;

8:

9: if (invocation.Method.IsSpecialName)

10: {

11: if (invocation.Method.DeclaringType ==

12: typeof(INotifyPropertyChanged))

13: {

14: if (methodName.StartsWith("add_"))

15: {

16: PropertyChanged+=(PropertyChangedEventHandler)

17: invocation.Arguments[0];

18: }

19: else

20: {

21: PropertyChanged-=(PropertyChangedEventHandler)

22: invocation.Arguments[0];

23: }

24:

25: return;

26: }

27:

28: if (methodName.StartsWith("set_"))

29: {

30: PropertyInfo propertyInfo = invocation.Proxy

31: .GetType().GetProperty(methodName.Substring(4));

32: object oldValue = propertyInfo.GetValue(

33: invocation.Proxy,

34: invocation.Arguments.SkipLast(1).ToArray());

35:

36: invocation.Proceed();

37:

38: if (oldValue == invocation.Arguments

39: [invocation.Arguments.Length - 1])

40: {

41: return;

42: }

43:

44: OnPropertyChanged(invocation.Proxy,

45: new PropertyChangedEventArgs(propertyInfo.Name));

46:

47: return;

48: }

49: }

50:

51: invocation.Proceed();

52: }

53:

54: private void OnPropertyChanged(object sender,

55: PropertyChangedEventArgs e)

56: {

57: PropertyChangedEventHandler eventHandler=PropertyChanged;

58:

59: if (eventHandler != null)

60: {

61: eventHandler(sender, e);

62: }

63: }

64: }

Последним шагом является вызов метода-расширения для MEF контекста приложения:

1: var catalog = new InterceptingCatalog(new DirectoryCatalog("."),

2: new InterceptionConfiguration().AddAopInterception());

Теперь любой класс, помеченный атрибутом [ExportMetadata( AopExtensions.AspectMetadata, Aspects.NotifyPropertyChanged)], при разрешении через IoC контейнер, автоматически получит реализацию INotifyPropertyChanged для своих виртуальных свойств.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]