C # Мониторинг классов с закрытым исходным кодом и примитивов для изменения значений - PullRequest
1 голос
/ 09 марта 2019

Мне нужно иметь возможность отслеживать большое количество объектов на предмет изменений.Почти в любом случае я мог бы просто использовать INotifyPropertyChanged и назвать это днем.Однако моя ситуация не так проста.Цель моего проекта - создать симуляцию, в которой любой объект может быть подключен, отслежен и доступен.

Подключение выполняется с помощью отражения с помощью Fluent API, который определяет свойства и поля для включения в среду моделирования.Это выглядит примерно так:

public void DeclareVariables( IVariableBuilder builder )
{
  builder.Include( x => x.PropertyA );
  builder.Include( x => x.FieldA );
}

Свойства и поля затем оборачиваются в класс Variable и сохраняются в классе Environment.

public class Variable<T> : IVariable
{
  private FieldInfo _field; // Properties are turned into FieldInfo via k__BackingField
  private object _obj;

  public string Id
  {
    get => $"{_obj.GetType}-{_field.Name}";
  }

  public T Value
  {
    get => _field.GetValue( _obj );
  }
}

public class Environment
{
  private Dictionary<string, IVariable> _variables;
}

Это правильно хранит ссылкико всем полям и свойствам, которые я пытаюсь включить. Однако я хочу иметь возможность отслеживать все эти изменения, и большинство включенных полей являются либо классами с закрытым исходным кодом, либо примитивами, которые не реализуют INotifyPropertyChanged` или не могут быть получены.

Я пришел из фона JavaScript, где вы можете сделать это, создав прокси-объект объекта, который может переопределять методы получения и установки свойств, а так как JavaScript имеет тип утка, вы можете просто заменить фактический экземпляр объекта на местепрокси.

Есть ли способ сделать то же самое в C #?Я полагаю, что правила безопасности типов в C # не допускают такой практики, особенно с примитивами.Однако, насколько мне известно, единственный способ отслеживать эти классы - это каким-то образом перехватывать операции, в которых устанавливается значение.

1 Ответ

3 голосов
/ 09 марта 2019

Короче говоря, это невозможно для произвольных объектов.

Когда вы делаете это в JavaScript, вы можете:

  • изменить существующий объект на местеНапример, замените одну конкретную функцию-член, поскольку нет ограничений на изменение
  • создайте новый прокси-объект

Опция 1 не может быть выполнена в C # для Произвольная объекты.

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

  1. Чтобы это работало, все взаимодействие должно проходить через прокси-объект.Если базовый объект изменяется напрямую, нет способа обнаружить изменения.
  2. Тип прокси-объекта может быть несовместим с базовым конкретным типом.Например, допустим, у вас есть запечатанная YourCustomClass, которую вы хотите обернуть.Прокси-класс, например, YourCustomClassProxy не имеет общего предка в иерархии, отличной от System.Object, поэтому вы не сможете заменить экземпляры YourCustomClass на экземпляры YourCustomClassProxy в вызовах методов и т. Д.хотя существуют зрелые библиотеки для создания динамических прокси (см. Castle Project ), они поддерживают интерфейсы и виртуальные члены, поскольку они могут быть перехвачены во время выполнения без нарушения контрактов типов.

Теоретически можно переписать IL , чтобы добавить ожидаемое поведение даже к закрытым классам, но я бы не назвал это практичным.

Подводя итог, доступны следующие стратегии:

  • составляют новые (прокси) объекты, реализующие INotifyPropertyChanged из тех, которые этого не делают, и применяют изменения только через прокси
  • , когдавыше не представляется возможным, запросить объекты для изменений.Предполагая, что у вас есть приложение с графическим интерфейсом, и что изменения вызваны взаимодействием с пользователем, вы можете иногда предвидеть, какие части графического интерфейса изменится и вызвать обновление.
...