我正在尝试在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。使用它的类会将其视为浮点型,并且不知道它在内部也是一个可观察量。
为此,我通常会重载赋值运算符,而不是执行以下操作:
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 变量被赋值时,它会进行赋值并调用一个函数。有什么方法可以做到这一点,同时保护使用类(示例中的产品(不知道变量是可观察的?
我认为你应该使用类似流畅的东西来实现灵活性,而不是重载基元类型及其运算符只是为了将它们注入到不属于它的地方:
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
附注:
如您所见,重载基元类型是个坏主意。它们在性质上与引用太不同了,这些引用很容易打乱您的整个逻辑。