2

假设我在课堂上有一些活动:

 class SomeClass{
     event ChangedEventHandler SomeDoubleChanged;
     double SomeDouble;
 }

和:

 delegate void ChangedEventHandler(double d);

现在假设我想监听 SomeDouble 上的更改事件,但只想触发大于delta. 现在我可以做类似的事情

 SomeObject.SomeDoubleChanged += (d) => {if(abs(d-old_d) > delta){
                                  //do something     
                                 };

但我希望我的事件能够处理这个问题,所以在最好的情况下我想做类似的事情:

 SomeObject.SomeDoubleChange += (delta, (d) => {});

并且仍然允许:

 SomeObject.SomeDoubleChange += (d) => {};

我唯一想到的实现这一点的唯一方法是删除整个 event 关键字并实现一个带有 += 和 -= 运算符的容器,对指定的委托进行操作。但在我看来,这不是一个非常优雅的解决方案,因为它让 SomeClass 的用户认为 SomeDoubleChanged 不是事件。

这个问题最优雅的解决方案是什么?

4

3 回答 3

5

(我建议不要在这里使用术语“lambda”,因为您也在使用 lambda 表达式。听起来您对更改感兴趣,即增量。)

您可以创建自己的静态方法来创建适当的委托:

public static ChangedEventHandler FilterByDelta(double delta,
                                                ChangedEventHandler handler)
{
    double previous = double.MinValue;
    return d =>
    {            
        if (d - previous > delta)
        {
            handler(d);
        }
        // Possibly put this in the "if" block? Depends on what you want.
        previous = d;
    };
}

然后你可以使用:

SomeObject.SomeDoubleChange += Helper.FilterByDelta(5, d => ...);

不幸的是,您不能在 lambda 表达式上使用扩展方法,这会使这更容易。

于 2013-01-21T12:38:20.597 回答
1

你可以这样写

SomeObject.SomeDoubleChange += (d) => DoWork(lambda, d, (d) => {});

private void DoWork(double minLambda, double currentValue, ChangedEventHandler handler)
{
  if(abs(currentValue - oldValue) > minLambda))
  {
     handler(currentValue);
  }
}
于 2013-01-21T12:38:40.127 回答
0

我最喜欢的“优雅”方式是使用可观察对象而不是事件,尤其是在Reactive Extensions的帮助下实现。想象一下能够这样做:

class SomeClass
{
    public ObservableProperty<double> SomeDouble { get; private set; }
    public SomeClass()
    {
        SomeDouble = new ObservableProperty<double>();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SomeClass someobject = new SomeClass();
        const double lam = 1.0;
        using (var sub = someobject.SomeDouble.Observable
            .TakeWhile((oldvalue, newvalue) => 
                Math.Abs(oldvalue - newvalue) > lam)
            .Subscribe(x => 
                Console.WriteLine("{0}\t{1}",x,someobject.SomeDouble.Value)))
        {
            someobject.SomeDouble.Value = 3.0;
            someobject.SomeDouble.Value = 2.0;
            someobject.SomeDouble.Value = 1.0;
            someobject.SomeDouble.Value = -1.0;
        }
    }
}

与输出

3       3
1       1
-1      -1

lambda 在这里是自定义扩展方法的一个参数TakeWhile

可观察属性的包装器可能看起来像这样(可能不是很完整):

/// Wrapper for properties that notify their change by means of an observable sequence
class ObservableProperty<T>
{
    T val;
    ISubject<T> sub = new Subject<T>();

    public T Value
    {
        get { return val; }
        set
        {
            val = value;
            sub.OnNext(value);
        }
    }

    public IObservable<T> Observable
    {
        get { return sub; }
    }
}

以及自定义扩展方法如下:

public static class Extensions
{
    /// Return distinct values from source while AdjacentCompare is true
    public static IObservable<TSource> TakeWhile<TSource>(this IObservable<TSource> source, Func<TSource, TSource, bool> AdjacentCompare)
    {
        return source.Scan((oldvalue, newvalue) =>
        {
            if (AdjacentCompare(oldvalue, newvalue))
                return newvalue;
            return oldvalue;
        }).DistinctUntilChanged();
    }
}

辅助类和扩展方法看起来比较长,但它们是通用的,即你如何比较你的值以及它们是什么类型并不重要(可能受实现的限制),你会得到 rx 的好处轻松控制订阅生命周期、线程安全和编写声明性代码。

前段时间搜索时,我遇到了这个库:ReactiveProperty

于 2013-01-21T20:34:31.927 回答