Автоматическая реализация INotifyPropertyChanged
От: Аноним  
Дата: 20.02.11 20:30
Оценка:
Привет всем, есть такая задача.

Есть коллекция элементов простых классов данных, типа:

class Data {
public String Property1 { get; set; }
public Int Property2 { get; set; }
// ... и т.д.
}

Необходимо реализовать класс, который бы автоматически оповещал об изменении свойств таких объктов.


public class PropertyChangedNotifier
    {
        
        List<Object> objects;

        public PropertyChangedNotifier(EventBroker eventBroker) { // ctor
            objects = new List<Object>()
        }

        public void Register(Object obj) {
            // Здесь как-то создать динамически события для регистрации изменений
        }

        public void Unregister(Object obj) {
            // Отписаться от этих событий
        }
        
        // Это событие должно вызываться
        public event мой_делегат_c_двумя_парметрами_Object_ИмяСвойства ObjectPropertyChanged;
    }


Буду регистрировать все мои объекты в объекте этого класса и обабатывать события ObjectPropertyChanged. Подскажите как можно реализовать методы Register и Unregister

Спасибо
Re: Автоматическая реализация INotifyPropertyChanged
От: adontz Грузия http://adontz.wordpress.com/
Дата: 20.02.11 21:10
Оценка:
Здравствуйте, Аноним, Вы писали:

Я бы начал с этого
http://www.rsdn.ru/article/dotnet/dotnetcontext.xml
Автор(ы): Тимофей Казаков
Дата: 20.12.2003
Контексты не являются чем-то принципиально новым. однако нельзя сказать, что их использование широко распространено. Статья показывает, что может дать разработчику использование этой технологии.

http://www.rsdn.ru/article/dotnet/cntxtvsrealproxy.xml
Автор(ы): Тимофей Казаков
Дата: 05.06.2004
В статье разбираются тонкости работы с контекстами в .NET. В частности, разбирается класс RealProxy, а также приводятся примеры использования контекстов. Статья является продолжением статьи "Механизмы контекстов в .NET" ( http://www.rsdn.ru/article/dotnet/dotnetcontext.html ) в RSDN Magazine 3'2003.



Получится ли и как будет работать — ХЗ.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: Автоматическая реализация INotifyPropertyChanged
От: perekrestov Украина  
Дата: 20.02.11 22:25
Оценка:
Здравствуйте, Аноним, Вы писали:
Implement InotifyPropertyChanged with Castle.DynamicProxy
Посмотрите аналогичную реализацию от Ayende DataBindingFactory тут
Re: Автоматическая реализация INotifyPropertyChanged
От: Sinix  
Дата: 21.02.11 00:58
Оценка: +2
Здравствуйте, Аноним, Вы писали:

А>Необходимо реализовать класс, который бы автоматически оповещал об изменении свойств таких объктов.

Я бы так не делал без особых на то оснований.
Идея сама по себе кривая: достаточно обратиться к объекту напрямую, без создаваемой обёртки — всё, события не будет. Если вы хотите инжектить il в рантайме — новая проблема: сеттеры могут содержать в себе логику. Да и изврат это: применять решения не-из-коробки для разгребания архитектурных проблем.

Если уж припёрло, посмотрите на решение из BLToolkit (может, применяется где-то ещё, не попадалось) — там используются классы с абстрактным геттером-сеттером и в рантайме создаётся наследник со всеми нужными фичами. Я бы вернулся к старой доброй кодогенерации.

А>Буду регистрировать все мои объекты в объекте этого класса и обабатывать события ObjectPropertyChanged.

А data binding тоже будет его обрабатывать?
Re[2]: Автоматическая реализация INotifyPropertyChanged
От: Аноним  
Дата: 21.02.11 08:22
Оценка: 9 (1)
А>>Необходимо реализовать класс, который бы автоматически оповещал об изменении свойств таких объктов.
S>Я бы так не делал без особых на то оснований.
S>Идея сама по себе кривая: достаточно обратиться к объекту напрямую, без создаваемой обёртки — всё, события не будет. Если вы хотите инжектить il в рантайме — новая проблема: сеттеры могут содержать в себе логику. Да и изврат это: применять решения не-из-коробки для разгребания архитектурных проблем.

S>Если уж припёрло, посмотрите на решение из BLToolkit (может, применяется где-то ещё, не попадалось) — там используются классы с абстрактным геттером-сеттером и в рантайме создаётся наследник со всеми нужными фичами. Я бы вернулся к старой доброй кодогенерации.


А>>Буду регистрировать все мои объекты в объекте этого класса и обабатывать события ObjectPropertyChanged.

S>А data binding тоже будет его обрабатывать?

Обещаю, что позже опишу из каких соображений мне это понадобилось. Может быть у меня действительно архитектурная проблема.
Re: Автоматическая реализация INotifyPropertyChanged
От: Tom Россия http://www.RSDN.ru
Дата: 21.02.11 10:06
Оценка: +3 :))) :))) :)
А>Спасибо
Сейчас придёт саи знаете кто и посоветует сами знаете что
Народная мудрось
всем все никому ничего(с).
Re[2]: Автоматическая реализация INotifyPropertyChanged
От: Codechanger Россия  
Дата: 21.02.11 11:12
Оценка:
Здравствуйте, Tom, Вы писали:

А>>Спасибо

Tom>Сейчас придёт саи знаете кто и посоветует сами знаете что

Сами знаете что не нужно
Re[2]: Автоматическая реализация INotifyPropertyChanged
От: Аноним  
Дата: 21.02.11 21:56
Оценка:
В общем, как и обещал, попробую раскрыть секреты моей архитектуры. Слабонервным просьба удалится )

Итак, есть некая модель данных, она представляет собой обычные свойства, что я уже описал, некоторые свойства — комплексные типы:

class DataModel {
public String Prop1 { get; set; }
public int Prop2 { get; set; }
public DataModelSubClass { get; set; } // комплексный тип
// ..
}

Коллекция таких объектов является сердцем приложения. Вся работа программы заключается в обработке/изменении данных этой модели. Программа состоит из нескольких частей, которые по-своему обрабатывают эти данные, по-своему их отображают. Разные части программы могут обрабатывать одни и те же поля. Позже программа будет расшираяемой (плагины), каждый плагин будет "смотреть" на модель под своим углом. Итак, имею в памяти коллекцию объектов, которую обрабатывается разными частями приложения разной логикой. Отмечу, плагины — это еще не представление с контроллером (хотя некоторые части приложения могут отображать данные напрямую, некоторые же части могут строить свои модели основываясь на этих данных — это к вопросу о DataBinding). Поэтому было принято решение избавить модель от какой-либо логики, сами плагины будут содержать эту логику. Но данные в коллекции меняются и нужно уведомлять об этом другие части программы (плагины). Кто их меняет им абсолютно фиолетово (если конечно это делают не они сами). Поэтому для оповещения об изменении элементов коллекции ввел механизм EventBroker. Изменяющая коллекцию часть обязана кинуть сообщение в брокер, подписчики обрабатывают их. Для изменения элементов коллекции и использую единственный сервис:

class DataModelService {
public DataModel CreateNew() {
// ..
}

public void AddToList(DataModel model) {
// .. добвляю в коллекцию
// .. где то здесь вызываю событие брокера Added
}

public DataModel DeleteFromList(DataModel model) {
// .. удаляю из коллекции
// .. вызываю событие через брокер Deleted
}
}

Все плагины для изменения элеметов в коллекции используют этот сервис, другого способа манипулирования элементами у них нет.
Здесь с моей точки зрения все ясно. Но как оповещать эти плагины об изменении полей(свойств) объектов? Опять же, самим плагинам абсолютно фиолетово кто это делает. Понадобился механизм оповещения. На первый вгляд — это патерн INotifyPropertyChanged. Но так как с элементами работают множество частей программы (плагины), захотелось как то упростить жизнь и избавиться от всяческих проблем связвнных с "нечистой" подпиской/отпиской на события. Решил сами плагины не совращять возможностью напрямую подписываться на собия IPropertyChanged, всесто этого решил использовать механизм EventBroker-а. Так как вся манипуляция элементами коллекции происходит через сервис DataModelService, решил, что можно сделать как-то так:

class DataModelService {
public DataModel CreateNew() {
// ..
}

public void AddToList(DataModel model) {
// .. добвляю в коллекцию
// .. где то здесь вызываю событие брокера Added

// !!! Как-то цепляюсь к свойсвам model, чтобы во время из изменений вызвать событие брокера ObjectPropertyChanged(объкт, свойство)
}

public DataModel DeleteFromList(DataModel model) {
// .. удаляю из коллекции
// .. вызываю событие через брокер Deleted

// !!! Отцепляюсь
}
}

Ну а плагины будут обрабатывать событие ObjectPropertyChanged и может быть по своему реагировать. Вот в принципе и все соображения. Может быть это не есть гуд, но мне показалось, что такой подход можеть быть предельно ясным и простым.

ЗЫ: Проект не коммерческий, делаю для души, поэтому волен в эксперементах как в плане архитектуры так и вообще
ЗЫ2: Т.к. проект душевный, между подходами к нему возникают значительные паузы. Пытаюсь иметь предельно простую и ясную архитектуру, что бы долго не вьезжать, что было сделано до этого.
Re[3]: Автоматическая реализация INotifyPropertyChanged
От: AlexNek  
Дата: 21.02.11 22:54
Оценка:
Здравствуйте, Аноним, Вы писали:

А>В общем, как и обещал, попробую раскрыть секреты моей архитектуры. Слабонервным просьба удалится )

Все равно тяжело понять концепт. В любом случае пытаться сделать универсальный перекрыватель сеттеров — дохлое дело. (Не в том смысле что нельзя, а том что прийдется только блох потом ловить)
А почему бы не сделать "проперти" исключительно объектами. string->MyDataString и манипулировать уже Set/GetValue для этих объектов.
Re[3]: Автоматическая реализация INotifyPropertyChanged
От: Sinix  
Дата: 22.02.11 01:05
Оценка: +1
Здравствуйте, Аноним, Вы писали:

Ок, понятно. Решение вполне рабочее, только у вас все внутренности торчат наружу.

Если хотите их спрятать, но при этом не выходить за рамки POCO-подхода, то вам придётся или писать кучу писанины, или использовать кодогенерацию, например, T4. Ничего сложного там нет — сгенерить к
partial class SomeData
{
  private int id;
  private string name;
}

довесок (псевдокод)
partial class SomeData: INotifyPropertyChanged
{
  public SomeData(int id) { // ...
  protected virtual void OnPropertyChanged(string propertyName) { // ...
  public int Id { get; set; }
}


Если не хотите возиться с подпиской/отпиской на события, связанные с отдельными объектами, несложно сгенерить аналог DataTable — унаследоваться от ObservableCollection (это если .Net 4) или от BindingList и добавить типизированные методы.

Единственное, что пока остаётся непонятным — как вы работаете с DAL и как вы собираетесь реализовать change tracking.
Re[3]: Автоматическая реализация INotifyPropertyChanged
От: HowardLovekraft  
Дата: 22.02.11 07:57
Оценка:
Здравствуйте, Аноним, Вы писали:

А>для оповещения об изменении элементов коллекции ввел механизм EventBroker


Прекрасно, но:
— INotifyCollectionChanged и INotifyPropertyChanged — стандарт, а ваш механизм — нет;
— на события брокера так же нужно подписываться и от них нужно отписываться;
— неочевиден механизм оповещения в случае усложнения структуры данных (модель содержит коллекцию моделей, каждая из которых содержит от 1 до N коллекций моделей и т. д);
— не понятно, как уведомить заинтересованные стороны об изменении данных, если изменение инициировано из модели;
— чтобы вы не придумали, по сравнению с INotifyPropertyChanged, механизм оповещения об изменении свойств будет выполнен в виде костыля;
— вам это нужно будет прикрутить к UI. То, что будет прикручиваться к UI (пусть и не сама model, а сферическая view model в вакууме, выставляемая плагином), скорее всего, будет реализовывать INotifyCollectionChanged и INotifyPropertyChanged. Т.е. у вас появится 2 разных механизма оповещения об изменении состяния данных;
— про change tracking Sinix уже спросил
Автор: Sinix
Дата: 22.02.11
— тоже не ясно.

А>захотелось как то упростить жизнь и избавиться от всяческих проблем связвнных с "нечистой" подпиской/отпиской на события

А>Пытаюсь иметь предельно простую и ясную архитектуру, что бы долго не вьезжать, что было сделано до этого

public class PluginBase<TModel> : IDisposable
  where TModel : INotifyPropertyChanged
{
  private readonly TModel model;

  public PluginBase(TModel model)
  {
    this.model = model;
    this.model.PropertyChanged += Model_PropertyChanged;
  }

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  private void Model_PropertyChanged(Sender object, PropertyChangedEventArgs args)
  {
    OnModelPropertyChanged(args.PropertyName);
  }

  protected virtual void OnModelPropertyChanged(String propertyName)
  {
    // TODO: реакция на изменения свойства модели
  }

  protected virtual void Dispose(bool disposing)
  {
    if (disposed)
      return;

    if (disposing)
    {
      model.PropertyChanged -= Model_PropertyChanged;
    }

    disposed = true;
  }
  private Boolean disposed;
}


Не? Куда проще?

З.Ы. T4 — наше все и даже чуть-чуть больше.
Re[4]: Автоматическая реализация INotifyPropertyChanged
От: Аноним  
Дата: 22.02.11 22:53
Оценка:
AN>А почему бы не сделать "проперти" исключительно объектами. string->MyDataString и манипулировать уже Set/GetValue для этих объектов.
Что-то не могу понять что имеется ввиду
Re[5]: Автоматическая реализация INotifyPropertyChanged
От: AlexNek  
Дата: 22.02.11 23:11
Оценка:
Здравствуйте, Аноним, Вы писали:


AN>>А почему бы не сделать "проперти" исключительно объектами. string->MyDataString и манипулировать уже Set/GetValue для этих объектов.

А>Что-то не могу понять что имеется ввиду
Примерно так

class DataItem
{
 public virtual object Value 
 {
    get {return null;} set ...;}
 }

class DataItemInteger
{
  int valueInt;
  public override object Value
  {
    get{ return valueInt;}
    set{ base.Value = value; valueInt = (int)value;}
  }
}


Тогда объект будет определятся через List<DataItem>, Модификация ловится в базовом классе. Начинить можно много еще чем. Список можно показать в проперти грид точно как-же как и объект с помощью доп. атрибутов и классов.
Re[4]: Автоматическая реализация INotifyPropertyChanged
От: Аноним  
Дата: 22.02.11 23:26
Оценка: +1
S>Если хотите их спрятать, но при этом не выходить за рамки POCO-подхода, то вам придётся или писать кучу писанины, или использовать кодогенерацию, например, T4. Ничего сложного там нет — сгенерить к
S>
S>partial class SomeData
S>{
S>  private int id;
S>  private string name;
S>}
S>

S>довесок (псевдокод)
S>
S>partial class SomeData: INotifyPropertyChanged
S>{
S>  public SomeData(int id) { // ...
S>  protected virtual void OnPropertyChanged(string propertyName) { // ...
S>  public int Id { get; set; }
S>}
S>


Да, пожалуй это единственный приемлемый вариант.

S>Если не хотите возиться с подпиской/отпиской на события, связанные с отдельными объектами, несложно сгенерить аналог DataTable — унаследоваться от ObservableCollection (это если .Net 4) или от BindingList и добавить типизированные методы.


Типизированные методы для чего? Имеете ввиду методы для манипуляции коллекцией (добавления/удаления элементов)? Если да, то вы наверное правы — ObservableCollection хороший базовый класс. Хотя я конечно изначально хотел иметь абсолютно "чистую" модель.

S>Единственное, что пока остаётся непонятным — как вы работаете с DAL и как вы собираетесь реализовать change tracking.


Есть (вернее будет) слой DAL, который будет считывать данные с диска, после этого они будут находится в памяти. Также можно будет сбросить данные на диск. Данные хранятся НЕ в базе данных, а в файловой системе. А вот с "change tracking" мне самому не все пока ясно. По идее возможны только два состояния: modified и not modified. После загрузки данных они находятся в состоянии — not modified, после сохранения, тоже принимают его. Я пока не углублялся, но мне кажется будет достаточно флага IsChanged (или нескольких флагов: IsChangedA, IsChangedB... для разных групп данных), которые помещу в модель. Понимаю, что в идеале должно что-то быть (под названием ChangesManager), что бы регистировало изменения в пропертях (и тут меня придавливает мысль, что без INotifyPropertyChanged не обойтись).

Извиняюсь за субмур — ночь, нужно подумать на свежую голову
Re[4]: Автоматическая реализация INotifyPropertyChanged
От: Аноним  
Дата: 22.02.11 23:53
Оценка:
HL>Прекрасно, но:
HL>- INotifyCollectionChanged и INotifyPropertyChanged — стандарт, а ваш механизм — нет;
HL>- на события брокера так же нужно подписываться и от них нужно отписываться;
Да, но подписка была бы разовой — предпологается, что плагины инициализируются единожды (при старте). Гемороем может стать подписка/отписка на изменения в пропертях, ведь элементы коллекции меняются по ходу работы программы.

HL>- неочевиден механизм оповещения в случае усложнения структуры данных (модель содержит коллекцию моделей, каждая из которых содержит от 1 до N коллекций моделей и т. д);

да, здесь можно себе круто усложнить жизнь. нужно подумать.

HL>- не понятно, как уведомить заинтересованные стороны об изменении данных, если изменение инициировано из модели;

в моем изначальном варианте, этого не дожно случаться — модель не содержит логики, только данные

HL>- чтобы вы не придумали, по сравнению с INotifyPropertyChanged, механизм оповещения об изменении свойств будет выполнен в виде костыля;

HL>- вам это нужно будет прикрутить к UI. То, что будет прикручиваться к UI (пусть и не сама model, а сферическая view model в вакууме, выставляемая плагином), скорее всего, будет реализовывать INotifyCollectionChanged и INotifyPropertyChanged. Т.е. у вас появится 2 разных механизма оповещения об изменении состяния данных;
Да может быть и 2 механизма, но меня это не пугает. Вообще, я бы хотел добиться по возможности простоты ядра, все сложности хотел бы иметь в плагинах.

HL>- про change tracking Sinix уже спросил
Автор: Sinix
Дата: 22.02.11
— тоже не ясно.


А>>захотелось как то упростить жизнь и избавиться от всяческих проблем связвнных с "нечистой" подпиской/отпиской на события

А>>Пытаюсь иметь предельно простую и ясную архитектуру, что бы долго не вьезжать, что было сделано до этого

HL>
HL>public class PluginBase<TModel> : IDisposable
HL>  where TModel : INotifyPropertyChanged
HL>{
HL>  private readonly TModel model;

HL>  public PluginBase(TModel model)
HL>  {
HL>    this.model = model;
HL>    this.model.PropertyChanged += Model_PropertyChanged;
HL>  }

HL>  public void Dispose()
HL>  {
HL>    Dispose(true);
HL>    GC.SuppressFinalize(this);
HL>  }

HL>  private void Model_PropertyChanged(Sender object, PropertyChangedEventArgs args)
HL>  {
HL>    OnModelPropertyChanged(args.PropertyName);
HL>  }

HL>  protected virtual void OnModelPropertyChanged(String propertyName)
HL>  {
HL>    // TODO: реакция на изменения свойства модели
HL>  }

HL>  protected virtual void Dispose(bool disposing)
HL>  {
HL>    if (disposed)
HL>      return;

HL>    if (disposing)
HL>    {
HL>      model.PropertyChanged -= Model_PropertyChanged;
HL>    }

HL>    disposed = true;
HL>  }
HL>  private Boolean disposed;
HL>}
HL>


HL>Не? Куда проще?


Пример красивый, спасибо. Но у меня коллекция элементов. Значить подписываться придется (в случае ObservableCollection) на CollectionChanged. Также придется подписывать и отписываться (во время удаления) на события каждого элемента коллекции. Хотя здесь возникли идеи, как можно зделать:
Если обернуть коллекцию в класс (что-то вроде ObservableCollection), что бы внутри этого класса подписываться на все проперти внутренних элементов, а на ружу выставлять обобщенное событие типа ObjectPropertyChanged(объект, проперти). Или имено это вы и имели ввиду в этом примере? Еще бы додумать как сделать chage tracking и можно вдариваться в программирование )))

HL>З.Ы. T4 — наше все и даже чуть-чуть больше.

)))))

Спасибо за советы.
Re[5]: Автоматическая реализация INotifyPropertyChanged
От: Sinix  
Дата: 23.02.11 03:34
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Типизированные методы для чего? Имеете ввиду методы для манипуляции коллекцией (добавления/удаления элементов)?

Ага.

А>Если да, то вы наверное правы — ObservableCollection хороший базовый класс. Хотя я конечно изначально хотел иметь абсолютно "чистую" модель.

Он хорош только для 4го фреймворка. До этого он жил в WindowsBase.dll и для универсальных коллекций явно не подходил.

POCO-подход хорош для абстрактных теоретиков и для легковесных фреймворков. Для тяжёлых задач, чем меньше вы пишете кода — тем лучше. Если хочется тру-легковесного внешнего вида, вы можете спрятать ObservableCollection внутрь класса, реализовать в последнем нужные интерфейсы и дёргать методы спрятанного ObservableCollection. Зачем только

S>>Единственное, что пока остаётся непонятным — как вы работаете с DAL и как вы собираетесь реализовать change tracking.


А>Есть (вернее будет) слой DAL, который будет считывать данные с диска, после этого они будут находится в памяти. Также можно будет сбросить данные на диск. Данные хранятся НЕ в базе данных, а в файловой системе.

А. Тогда всё на порядок легче. Единственно, я бы предусмотрел возможность переехать на встроенную СУБД — вдруг припрёт?

А>А вот с "change tracking" мне самому не все пока ясно. По идее возможны только два состояния: modified и not modified.

Ещё Added, Deleted и хранение исходных значений — для optimistic concurrency. Если класс-коллекция и класс с собсно данными реализуют INotify***Changed, то трекер спокойно можно реализовать в виде отдельного класса.

А>Извиняюсь за субмур — ночь, нужно подумать на свежую голову

Не, всё в порядке. Вы только не воспринимайте мои советы как абсолютное знание — я ничего не знаю про ваши условия и ограничения. Может, для вас удобнее другие варианты будут. Например можно использовать датасет — всё нужное (и ненужное) уже есть. Только борьба с глюкавостью и убогостью датасета съест почти весь выигрыш
Re[2]: Автоматическая реализация INotifyPropertyChanged
От: _FRED_ Черногория
Дата: 23.02.11 09:33
Оценка:
Здравствуйте, Tom, Вы писали:

А>>Спасибо

Tom>Сейчас придёт саи знаете кто и посоветует сами знаете что

Самое печальное, что он совершенно справедливо окажется д`Артаньяном на коне при шпаге и в смокинге
Help will always be given at Hogwarts to those who ask for it.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.