用于观察者模式的 C# 浮动包装器
C# Float Wrapper For Use In Observer Pattern
我正在尝试在 C# 中实现可观察的本机数据类型(float、int、string、List)。我应该补充一点,我是 C# 的新手并且有 C++ 背景。
我的第一个想法是有一个像这样的可观察界面:
public interface IObservable {
void registerObserver(IObserver observer);
void deregisterObserver(IObserver observer);
void notifyObservers();
}
然后为重载运算符的本机数据类型(例如 float)创建包装器,以便(大部分)无缝使用:
Float f1 = new Float(0);
Float f2 = f1 + 2;
这必须是一个抽象基础 class,因为接口不能重载运算符。
然后我想创建一个 ObservableFloat,它结合了这两种想法,但在其状态发生任何变化时调用 notifyObservers。 class 使用它会将它视为 Float,并且不知道它在内部也是一个 Observable。
为此,我通常会重载赋值运算符,而不是像这样做:
return new Float(f1.value + f2.value);
它将修改第一个实例并 return 它。但是由于赋值似乎总是会改变引用,所以我会在第一次赋值操作时丢失我的可观察对象及其观察者列表,对吗?
我发现这个 post 在 C# 中使用事件实现观察者:link
此代码:
private State _state;
public State MyState
{
get { return _state; }
set
{
if (_state != value)
{
_state = value;
Notify();
}
}
}
基本上做我想要的:当状态变量被分配一个新值时,它会进行分配并调用一个函数。有什么方法可以实现这一点,同时避免使用 class(示例中的产品)知道变量是 Observable?
我认为你应该使用类似流利的东西来获得灵活性,而不是重载基本类型及其运算符只是为了将它们注入到某个地方它不属于:
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;
}
在代码中它看起来像这样,并且不会在赋值时中断,但要注意下面的小技巧,它可能会让你在使用引用时感到困惑(omg 这将是不可能的调试):
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
PS:
如您所见,重载基本类型不是个好主意。它们在本质上与引用太不一样了,引用很容易打乱你的整个逻辑。
我正在尝试在 C# 中实现可观察的本机数据类型(float、int、string、List)。我应该补充一点,我是 C# 的新手并且有 C++ 背景。
我的第一个想法是有一个像这样的可观察界面:
public interface IObservable {
void registerObserver(IObserver observer);
void deregisterObserver(IObserver observer);
void notifyObservers();
}
然后为重载运算符的本机数据类型(例如 float)创建包装器,以便(大部分)无缝使用:
Float f1 = new Float(0);
Float f2 = f1 + 2;
这必须是一个抽象基础 class,因为接口不能重载运算符。
然后我想创建一个 ObservableFloat,它结合了这两种想法,但在其状态发生任何变化时调用 notifyObservers。 class 使用它会将它视为 Float,并且不知道它在内部也是一个 Observable。
为此,我通常会重载赋值运算符,而不是像这样做:
return new Float(f1.value + f2.value);
它将修改第一个实例并 return 它。但是由于赋值似乎总是会改变引用,所以我会在第一次赋值操作时丢失我的可观察对象及其观察者列表,对吗?
我发现这个 post 在 C# 中使用事件实现观察者:link 此代码:
private State _state;
public State MyState
{
get { return _state; }
set
{
if (_state != value)
{
_state = value;
Notify();
}
}
}
基本上做我想要的:当状态变量被分配一个新值时,它会进行分配并调用一个函数。有什么方法可以实现这一点,同时避免使用 class(示例中的产品)知道变量是 Observable?
我认为你应该使用类似流利的东西来获得灵活性,而不是重载基本类型及其运算符只是为了将它们注入到某个地方它不属于:
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;
}
在代码中它看起来像这样,并且不会在赋值时中断,但要注意下面的小技巧,它可能会让你在使用引用时感到困惑(omg 这将是不可能的调试):
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
PS:
如您所见,重载基本类型不是个好主意。它们在本质上与引用太不一样了,引用很容易打乱你的整个逻辑。