2

I am trying to implement observable native data types in C# (float, int, string, List). I should add that I am fairly new to C# and come from a C++ background.

My first thought was to have an observable interface like so:

public interface IObservable {

    void registerObserver(IObserver observer);

    void deregisterObserver(IObserver observer);

    void notifyObservers();
}

And then to create a wrapper for a native data type (for example float) that overloads the operators so it is (mostly) seamless to use:

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

This would have to be an abstract base class since interfaces can't overload operators.

Then I wanted to create an ObservableFloat which combined both ideas but called notifyObservers on any change to its state. The class using it would see it as a Float and would have no idea it was internally also an Observable.

For this I would normally overload the assignment operator so instead of doing something like:

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

It would modify the first instance and return it. But since assignment seems to always change the reference, I would loose my observable and its list of observers on the first assignment operation, right?

I found this post that implements an observer using events in C#: link This code:

private State _state;

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

Does essentially what I want: when the State variable gets assigned a new value it does the assignment and also calls a function. Is there any way you can think of to achieve this while shielding the using class (Product in the example) from knowing that the variable is an Observable?

aspirino67
  • 375
  • 5
  • 16

1 Answers1

1

I think you should use something fluent-like for flexibility instead of overloading primitive types and their operators just to inject them somewhere it doesn't belong:

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

So, for instance you will get something like this inside, if you want to copy primitive types behavior:

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

And if you want to work with instance in fluent style and notify observers:

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

In code it will look like this, and will not break on assignment, but beware of little trick below which can confuse you when working with references (omg it will be impossible to debug):

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

And in primitive form:

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

PS:

As you see, it is bad idea to overload primitive types. They just too different in nature from references which can easly shuffle your entire logic.

eocron
  • 6,885
  • 1
  • 21
  • 50
  • Right, this was the other option I thought. I will have to go this way then. Thanks! – aspirino67 Aug 20 '18 at 15:54
  • Since this implementation already makes clear that you are working with an object, wouldn't it be clearer to make all INumeric return void so you can tell at a glance that they are modifying the object? – aspirino67 Aug 20 '18 at 15:58
  • 1
    Yeah, it is clearer if you want to show that specific aspect. But, I added return of INumeric just for method-chaining. It simplifies code to that of writing exact sentences which describes actions like *"do that, then that, then another"* without excessive repeating boilerplate code. More here - https://stackoverflow.com/questions/1119799/method-chaining-in-c-sharp – eocron Aug 20 '18 at 18:03