10

我正在使用一个我无法编辑的类,它有一个属性(一个布尔值),当它发生更改时很高兴收到通知,我无法编辑属性获取或设置,因为我从 . dll(我没有代码)。

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

Additional
仅在其自己的类中更改,直接更改为底层私有变量。

例如

private bool m_MyValue = false;

public bool MyValue
  {
  get { return m_MyValue; }
  }

private void SomeFunction()
  {
  m_MyValue = true;
  }
4

8 回答 8

10

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

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

于 2009-08-06T10:44:13.340 回答
5

您不能直接执行此操作 [正如 Jon Skeet 所说],除非它是虚拟的,否则您可以拦截该类的所有实例创建,并且不会更改影响 propget 的真正“价值”的支持字段。

暴力破解的唯一方法是使用 Mono.Cecil 或 MS CCI在 Cecil 上使用这个 DimeCast来检测道具设置器。(或 PostSharp)

但是,这不会捕获对支持字段的内部更改(如果有的话)。(这就是为什么包装可能不起作用)。

更新:鉴于您肯定试图捕获底层字段更改的更新,唯一的方法是使用 PS / CCI / Cecil 并分析流程以拦截所有字段更新。总之,不是很可行。

于 2009-08-06T10:43:06.807 回答
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();
    }
}

显然,您可以将此代理模式构建到合适的对象中——您只需要知道所有调用都必须通过代理才能在您期望的时候引发事件。

于 2009-08-06T12:00:10.147 回答
2

如前所述,最直接的方法(并且需要对代码进行最少更改的方法)是使用 AOP 库,例如 PostSharp。

但是,使用依赖属性模式可以使用传统的 C#/.NET 实现解决方案,在整个 WPF 中使用效果很好。我建议阅读此内容,并考虑为您的项目实施这样的系统(或至少是其简化版本),如果合适的话。

于 2009-08-06T12:04:15.317 回答
1

你能从类继承并隐藏属性吗?像这样的东西...

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

        set
        {
            base.MyProperty = value;
            RaiseMyPropertyChangedEvent();
        }
    }
}
于 2009-08-06T10:47:11.120 回答
1

您将需要创建一个将类包装在 dll 中的类,在 setter 属性中,只需使用普通方法在那里引发一个事件。

于 2009-08-06T10:44:20.533 回答
0

我认为 Alex 的包装器想法很好,但是,考虑到对您来说最重要的是您知道值在使用之前已更改,您可以简单地将通知移动到 getter,避免内部值更改的担忧. 这样你就可以得到类似于轮询但可靠的东西:

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;
        }
    }
}
于 2009-08-06T12:11:52.650 回答
-1

您可以尝试继承它并使用它的孩子而不是它。覆盖属性的“集合”,使其引发事件。

编辑:...仅当属性在父类中是虚拟的...

于 2009-08-06T10:44:35.430 回答