简体   繁体   English

赋予代表属性的优雅方式

[英]Elegant way to give a delegate properties

Suppose I have some event in a class: 假设我在课堂上有一些事件:

 class SomeClass{
     event ChangedEventHandler SomeDoubleChanged;
     double SomeDouble;
 }

with: 有:

 delegate void ChangedEventHandler(double d);

Now suppose I want to listen for change events on SomeDouble, but only want to trigger changes greater than delta . 现在假设我想在SomeDouble上侦听更改事件,但只想触发大于delta更改。 Now I could do something like 现在我可以做类似的事情

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

But I want my event to handle this, so in the best case I want to do something like: 但是我希望我的事件能够解决这个问题,所以在最好的情况下,我想执行以下操作:

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

and still allow: 并且仍然允许:

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

The only way I only way I thought of implementing this, is to drop the whole event keyword and implement a container with += and -= operators, operating on the specified delegates. 我想到的唯一实现此方法的方法是删除整个事件关键字,并使用+ =和-=运算符实现一个容器,该容器对指定的委托进行操作。 But in my opinion this is not a very elegant solution, as it gives the user of SomeClass the idea that SomeDoubleChanged is no event. 但是我认为这不是一个非常优雅的解决方案,因为它使SomeClass的用户认为SomeDoubleChanged不是事件。

What would be the most elegant solution to this problem? 解决这个问题的最优雅的方法是什么?

(I'd recommend not using the term "lambda" here given that you're also using lambda expressions. It sounds like you're interested in the change, ie the delta.) (鉴于您也在使用lambda表达式,因此我建议您不要在这里使用术语“ lambda”。听起来您对更改(即增量)感兴趣。)

You could create your own static methods to create appropriate delegates: 您可以创建自己的静态方法来创建适当的委托:

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

Then you can use: 然后,您可以使用:

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

Unfortunately you can't use extension methods on lambda expressions, which would make this easier. 不幸的是,您不能在lambda表达式上使用扩展方法,这会使此操作变得更容易。

You can write someting like this 你可以这样写东西

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

private void DoWork(double minLambda, double currentValue, ChangedEventHandler handler)
{
  if(abs(currentValue - oldValue) > minLambda))
  {
     handler(currentValue);
  }
}

My favorite "elegant" way would be by using observables instead of events, especially, implemented with the help of Reactive Extensions . 我最喜欢的“优雅”方式是通过使用可观察对象而不是事件,尤其是在Reactive Extensions的帮助下实现的。 Imagine being able to do so: 想象一下能够做到这一点:

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

with the output 与输出

3       3
1       1
-1      -1

The lambda is here a parameter of the custom extension method TakeWhile . lambda是自定义扩展方法TakeWhile的参数。

The wrapper for observable properties would can look like that (possibly not quite complete): 可观察属性的包装可能看起来像这样(可能不太完整):

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

And the custom extension method as follows: 和自定义扩展方法如下:

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

The helper class and the extension method can look comparatively lengthy, but those are generic, ie it doesn't matter how you compare your values, and what type they are (limited by the implementation, perhaps), and you get the rx benefits of controlling subscription lifetime easily, thread safety, and writing declarative code. 辅助类和扩展方法看起来可能比较冗长,但是它们是通用的,即,无论您如何比较值以及它们是什么类型(可能受实现限制)都无关紧要,并且您可以获得rx的好处轻松控制订阅生命周期,线程安全和编写声明性代码。

While searching some time ago I came across this library: ReactiveProperty 前一段时间搜索时,我遇到了这个库: ReactiveProperty

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM