Search code examples
c#metaprogrammingproxy-pattern

C# Monitoring closed-source classes and primitives for value changes


I need to be able to monitor a large number of objects for changes. In nearly any case, I could just use INotifyPropertyChanged and call it a day. However, my situation isn't as simple. The goal of my project is to create a simulation where any object can be plugged in, monitored, and accessed.

The plugging in is done via reflection using a Fluent API that designates properties and fields to include in the simulation's environment. It looks something like this:

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

The properties and fields are then wrapped in a Variable class and stored in an Environment class.

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;
}

This properly stores references to all of the fields and properties I am trying to include. However, I want to be able to monitor all of these for changes, and most of the included fields are either closed-source classes or primitives, both of which do not implement INotifyPropertyChanged` or cannot be derived.

I come from a JavaScript background, where you can accomplish this by creating an object proxy that can override property getters and setters, and since JavaScript is duck-typed, you can just replace the actual object instance in-place with the proxy.

Is there a way to do the same in C#? I'd imagine C#'s type safety rules would not allow a practice like this, especially with primitives. As far as I am aware, however, the only way to monitor these classes would be to somehow intercept operations where the value is being set.


Solution

  • Put short, it's not possible for arbitrary objects.

    When you do this in JavaScript, you can:

    • alter an existing object in place, e.g. replace one specific member function, as there are no modification restrictions
    • create a new proxy object

    Option one can't be done in C# for arbitrary objects.

    Going for option two you can wrap an arbitrary instance with a proxy object with custom change detection logic. However, there are some caveats:

    1. For this to work, all interaction has to go through the proxy object. If the underlying object is modified directly, there is no way to detect changes.
    2. Type of the proxy object may be incompatible with the underlying concrete type. For example, let's say you have a sealed YourCustomClass which you want to wrap. The proxy class, e.g. YourCustomClassProxy doesn't share a common ancestor in the hierarchy other than System.Object, so you will not be able to substitute instances of YourCustomClass with instances of YourCustomClassProxy in method calls, etc. So while there are mature libraries for creating dynamic proxies (see Castle Project), they support interfaces and virtual members, because they can be intercepted in runtime without violating type contracts.

    In theory it's possible to rewrite IL to add behavior you expect even to closed classes, but I wouldn't call it practical.

    Summarizing, strategies available are:

    • compose new (proxy) objects implementing INotifyPropertyChanged from those that do not, and apply changes through proxies only
    • when above is not possible, query objects for changes. Assuming you have a GUI app and that changes are caused by user interaction, you can sometimes foresee which pieces of GUI would change and force a refresh.