C#监视封闭源类和基元的值更改

时间:2019-03-09 18:27:59

标签: c# metaprogramming proxy-pattern

我需要能够监视大量对象的更改。在几乎任何情况下,我都可以只使用INotifyPropertyChanged并称之为一天。但是,我的情况并非如此简单。我的项目的目标是创建一个模拟,可以在其中插入,监视和访问任何对象。

使用Fluent API通过反射完成插入,该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 个答案:

答案 0 :(得分:3)

简而言之,任意对象是不可能的。

使用JavaScript进行操作时,您可以:

  • 更改现有对象,例如替换一个特定的成员函数,因为没有修改限制
  • 创建一个新的代理对象

对于任意对象 ,在C#中无法完成选项一。

在选项二中,您可以使用具有自定义更改检测逻辑的代理对象包装任意实例。但是,有一些警告:

  1. 要使其正常工作,所有交互都必须通过代理对象。如果直接修改基础对象,则无法检测到更改。
  2. 代理对象的类型可能与基础具体类型不兼容。例如,假设您有一个要包装的密封YourCustomClass。代理类,例如YourCustomClassProxy在除System.Object之外的层次结构中不共享公共祖先,因此您将无法在方法调用等中用YourCustomClass的实例替换YourCustomClassProxy的实例。 因此,尽管有成熟的库可以创建动态代理(请参见Castle Project),但它们支持接口和虚拟成员,因为它们可以在运行时被拦截而不会违反类型协定。

从理论上讲,可以rewrite IL向封闭类添加您期望的行为,但是我不认为这是实用的。

总结一下,可用的策略是:

  • 从那些没有实现的{}对象组成新的(代理)对象,并仅通过代理应用更改
  • 如果无法执行上述操作,请查询对象以进行更改。假设您有一个GUI应用程序,并且更改是由用户交互引起的,则有时您可以预见哪些GUI会更改并强制刷新。