2009-08-06 45 views
10

我正在使用一个我无法编辑的类,它有一个属性(布尔型),它会很好地通知它何时发生变化,我无法编辑属性获取或设置,因为我正在导入该类从一个.dll(我没有代码)。在属性上触发事件/功能? (C#)

如何创建在属性更改时触发的事件/函数?

其他
它只是在它自己的类中直接更改为基础私有变量。

E.g.

private bool m_MyValue = false; 

public bool MyValue 
    { 
    get { return m_MyValue; } 
    } 

private void SomeFunction() 
    { 
    m_MyValue = true; 
    } 

回答

10

你不能,基本上......不是没有使用类似于调试器API的东西来在执行时注入代码并修改原始库的IL(我不推荐这些解决方案之一;除了任何东西否则可能违反图书馆的许可)。

基本上,如果一个属性不支持通知,它不支持通知。你应该寻找一种不同的方式来解决你的问题。 (例如,投票是否工作?)

+0

我想我将不得不走下来的投票路线,不是我所期待的,因为数据可以在调查时机来reduntant(我想要在数据被其他功能使用之前捕获数据) – ThePower 2009-08-06 10:52:19

+0

或者用你自己的支持INotify或你自己的实现的类来包装类? – Pondidum 2009-08-07 10:11:01

+0

@安迪:这取决于。如果你这样做,那么你不能使用类作为原始类型,代码中的其他位可能需要。 – 2009-08-07 10:37:46

5

你不能直接这样做[为乔恩斯基特说],除非它是虚拟的,你在一个位置拦截类的所有实例的创作,并有到影响实际支持字段没有变化propget的'价值'。

蛮力的唯一方法是使用Mono.Cecil或MS CCI来测试道具设定器la this DimeCast on Cecil。 (或PostSharp)

但是,这不会捕获对后台字段的内部更改(如果甚至有一个)。 (这是为什么包装可能不会工作)。

更新:鉴于你的更新,你肯定试图陷入潜在的领域变化,唯一的方法是使用PS/CCI /塞西尔和分析流来拦截所有现场更新。总之,不是很可行。

1

您将需要创建一个类,该类包装在dll中的类,在setter属性内使用常规方法在该处引发事件。

+2

但是,这不会通过其setter捕获组件所引起的内部更新,也不会直接更改poptabop反映的内部状态。 – 2009-08-06 10:49:08

-1

您可以尝试继承它并使用它的孩子而不是它。 重写属性的“set”,以便引发事件。

编辑:......只有当财产是在父类中的虚...

+0

唯一可行的是该属性被标记为虚拟... – 2009-08-06 12:13:13

+0

是...我同意...对于错误的人抱歉... – 2009-08-07 07:58:07

1

你能不能从类继承和隐藏属性?事情是这样的......

class MyClass : BaseClass 
{ 
    // hide base property. 
    public new bool MyProperty 
    { 
     get 
     { 
      return base.MyProperty; 
     } 

     set 
     { 
      base.MyProperty = value; 
      RaiseMyPropertyChangedEvent(); 
     } 
    } 
} 
+0

同http://stackoverflow.com/questions/1238137/creating-an-事件上一个属性-C/1238154#1238154。 (并更新到Q意味着这将无法正常工作) – 2009-08-06 10:52:59

+0

唯一可行的是该属性被标记为虚拟... – 2009-08-06 12:13:50

+1

@Thomas:不,'新'修饰符关键字不需要基地是虚拟的。 – 2009-08-06 12:19:02

3

可以说,这样做的唯一途径是创造某种“守望者”组成的,在一个单独的线程中运行,其工作是在间隔读取属性和提高物业价值发生变化时的事件。当然,这个解决方案会在线程和同步的黑暗水域中航行。

假设您的应用程序对于此对象是单线程的,您最简洁的解决方案是通过代理对象对此对象进行方法调用。它会检查财产的前后状态,并在事件发生变化的情况下提出事件。

这里是我说的什么一个简单的例子:

public class SomeProxy 
{ 
    public SomeProxy(ExternalObject obj) 
    { 
     _obj = obj; 
    } 

    public event EventArgs PropertyChanged; 

    private bool _lastValue; 

    private ExternalObject _obj; 

    protected virtual void OnPropertyChanged() 
    { 
     if(PropertyChanged != null) 
      PropertyChanged(); 
    } 

    protected virtual void PreMethodCall() 
    { 
     _lastValue = _obj.SomeProperty; 
    } 

    protected virtual void PostMethodCall() 
    { 
     if(_lastValue != _obj.SomeProperty) 
      OnPropertyChanged(); 
    } 

    // Proxy method. 
    public SomeMethod(int arg) 
    { 
     PreMethodCall(); 
     _obj.SomeMethod(arg); // Call actual method. 
     PostMethodCall(); 
    } 
} 

很明显,你可以建立这种代理模式到合适的对象 - 你只需要知道所有呼叫不得不经历当你期望它成为事件的代理人时。

+1

+1对于完整的例子(OP让他很清楚他想把它当作一个黑盒子,所以答案不适合IMNSHO) – 2009-08-06 14:11:15

2

如前所述,最直接的方法(以及需要对代码进行最少更改的方法)是使用诸如PostSharp之类的AOP库。

然而,使用传统的C#/ .NET通过使用dependency property pattern可以实现一个解决方案,通过WPF使用,效果很好。我建议阅读本文,并考虑为您的项目实施此类系统(或至少其简化版本),如果适用的话。

0

我认为亚历克斯的包装理念是好的,但是,鉴于最重要的是你知道在使用之前价值被改变了,你可以简单地将通知移动到吸气剂,规避内部价值变化的担忧。这样,你得到类似的东西投票而可靠:

class MyClass : BaseClass 
{ 
    //local value 
    private bool local; 

    //first access yet? 
    private bool accessed = false; 

    // Override base property. 
    public new bool MyProperty 
    { 
     get 
     { 
      if(!accessed) 
      { 
       // modify first-get case according to your taste, e.g. 
       local = base.MyProperty; 
       accessed = true; 
       RaiseMyPropertyChangedBeforeUseEvent(); 
      } 
      else 
      { 
       if(local != base.MyProperty) 
       { 
        local = base.MyProperty; 
        RaiseMyPropertyChangedBeforeUseEvent(); 
       } 
      } 

      return local; 
     } 

     set 
     { 
      base.MyProperty = value; 
     } 
    } 
}