用于观察者模式的 C# 浮点包装器

C# Float Wrapper For Use In Observer Pattern

提问人:aspirino67 提问时间:8/20/2018 更新时间:8/20/2018 访问量:647

问:

我正在尝试在 C# 中实现可观察的本机数据类型(float、int、string、List)。我应该补充一点,我对 C# 相当陌生,并且来自 C++ 背景。

我的第一个想法是有一个可观察的接口,如下所示:

public interface IObservable {

    void registerObserver(IObserver observer);

    void deregisterObserver(IObserver observer);

    void notifyObservers();
}

然后,为重载运算符的本机数据类型(例如浮点型)创建一个包装器,以便(大部分)无缝使用:

Float f1 = new Float(0);
Float f2 = f1 + 2;

这必须是一个抽象基类,因为接口不能重载运算符。

然后我想创建一个 ObservableFloat,它结合了这两个想法,但在状态发生任何更改时调用 notifyObservers。使用它的类会将其视为 Float,并且不知道它在内部也是一个 Observable。

为此,我通常会重载赋值运算符,而不是执行以下操作:

return new Float(f1.value + f2.value);

它将修改第一个实例并返回它。但是,由于赋值似乎总是会改变引用,因此我会在第一次赋值操作中丢失我的可观察对象及其观察者列表,对吧?

我找到了这篇使用 C# 中的事件实现观察器的帖子:链接此代码:

private State _state;

public State MyState
{
    get { return _state; }
    set
    {
        if (_state != value)
        {
            _state = value;
            Notify();
        }
    }
}

基本上做了我想要的:当 State 变量被分配一个新值时,它会执行赋值并调用函数。有没有什么方法可以做到这一点,同时保护 using 类(示例中的 Product)知道变量是 Observable?

C# 事件 运算符重载 observer-pattern 赋值运算符

评论


答:

1赞 eocron 8/20/2018 #1

我认为您应该使用类似 fluent 的东西来提高灵活性,而不是重载原始类型及其运算符只是为了将它们注入不属于它的地方:

public interface INumeric
{
    INumeric Add(INumeric num);
    INumeric Sub(INumeric num);
    INumeric Mul(INumeric num);
    INumeric Div(INumeric num);   
}

因此,例如,如果您想复制原始类型行为,您将在内部获得类似这样的东西:

public INumeric Sub(INumeric b)
{
    var a = this;
    return new Numeric(a.Value-b.Value);//obviuously, no notify here
}

如果您想以流畅的风格处理实例并通知观察者:

public INumeric Sub(INumeric b)
{
    var a = this;
    a.Value = a.Value - b.Value;//notify 'a' observers or whatever here.
    return a;
}

在代码中,它看起来像这样,并且不会在赋值时中断,但要注意下面的小技巧,这些技巧在处理引用时可能会让您感到困惑(天哪,调试是不可能的):

var a = new Numeric(10);
var b = new Numeric(2);

a = a.Sub(b)//10 - 2 = 8
     .Add(a)//8 + 8 = 16
     .Add(b);//16 + 2 = 18

并以原始形式:

a = a - b + a + b; //10 - 2 + 10 + 2 = 20

附言:

如您所见,重载基元类型是个坏主意。它们在本质上与引用太不同了,引用可以很容易地打乱你的整个逻辑。

评论

0赞 aspirino67 8/20/2018
是的,这是我想到的另一种选择。那我就得走这条路了。谢谢!
0赞 aspirino67 8/20/2018
由于此实现已经清楚地表明您正在使用对象,因此将所有 INumeric 返回 invalid 设置为无效,以便您可以一目了然地知道他们正在修改对象,这不是更清楚吗?
1赞 eocron 8/21/2018
是的,如果您想展示该特定方面,那就更清楚了。但是,我添加了 INumeric 的返回值,只是为了方法链接。它将代码简化为编写精确的句子,这些句子描述了诸如“做那个,然后那个,然后是另一个”之类的操作,而无需过多地重复样板代码。更多内容 - stackoverflow.com/questions/1119799/method-chaining-in-c-sharp