C# Object property reference

Is it possible to "bind" the properties of two different instances of the same object?

For example;

class bar{
public int MyInt {get;set;}
public var foo {get;set;}
}
------------------------------------------
class MainProgram{
bar Bar1 = new bar();
bar Bar2 = new bar();
bar Bar3 = new bar();

//some logic here to decide which objects properties needs to get binded (in our case it could be Bar1 and Bar2)
}

I want that when I change bar1.MyInt or bar2.myInt, the other one gets modified to the same value aswell. The thing is that only MyInt property needs to be binded. So bar.foo should stay different for each instance.

I mean it could be done with some loops and such but there are many instances of the same object in the app. And the speed is important, so is there any way I could do this like one would do in c++ with references/pointers.


Solution 1:

You can do it using INotifyPropertyChanged, but it's going to take a lot of boilerplate code.

Step 1: Create a base implementation of INotifyPropertyChanged:

internal abstract class Notifier:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

Step 2: Inherit the implementation in your data object and invoke NotifyPropertyChanged whenever a property is updated:

class bar:Notifier
{
    private int _myInt;
    public int MyInt
    {
        get=>_myInt;
        set
        {
            _myInt = value;
            NotifyPropertyChanged();
        }
    }
    private string _foo;
    public string foo
    {
        get=>_foo;
        set
        {
            _foo = value;
            NotifyPropertyChanged();
        }
    }
}

Step 3 (optional but saves you some work): write an extension method allowing you to (more easily) invoke callbacks on PropertyChanged events for objects that implement INotifyPropertyChanged:

internal static class Extensions
{
    public static void BindProperty(this INotifyPropertyChanged viewModel, string propertyName, Action changeAction) 
    {
        viewModel.PropertyChanged += (p, e) =>
        {
            if (e.PropertyName == propertyName) changeAction();
        };
    }
}

Step 4: Define your binding behavior between two instances of bar:

In the bar class:

public void Bind(bar twin)
{
    //whenever twin.MyInt changes, update our value as well
    twin.BindProperty(nameof(MyInt), () =>
    {
        _myInt = twin.MyInt;
    });

    //whenever twin.Foo changes, update our value as well
    twin.BindProperty(nameof(foo), () =>
    {
        _foo = twin.foo;
    });
}

In your main program bind the two instances to each other:

public static void Main()
{
    bar Bar1 = new bar();
    bar Bar2 = new bar();


    Bar1.Bind(Bar2);//property changes on Bar2 now update Bar1
    Bar2.Bind(Bar1);//property changes on Bar1 now update Bar2
}

Note: This approach has its limitations. The biggest is that property changed events will not "chain", meaning if I bind a 3rd bar, Bar3, to Bar2 (but not Bar1) and I update a property on Bar1, Bar 3 will not update its matching property. In order to twin all three you would have to link each bar to each of the other two bars. Of course, there are workarounds:

Is it some small subset of bars that you need to bind to each other? You can create all the connections programatically:

This relies on the NotifyPropertyChanged boilerplate code

    public static void BindMany(params bar[] bars) => BindMany(bars);

    public static void BindMany(IEnumerable<bar> bars)
    {
        foreach (var bar1 in bars)
        {
            foreach (var bar2 in bars)
            {
                if (bar1 != bar2) bar1.Bind(bar2);
            }
        }
    }

Usage:

public static void Main()
{
    bar Bar1 = new bar();
    bar Bar2 = new bar();
    bar Bar3 = new bar();
    bar Bar4 = new bar();
    bar Bar5 = new bar();

    BindMany(bar1, bar2, bar3);//bars 1-3 now always share the same property values
    BindMany(bar4, bar5);//bars 4-5 now always share the same property values, but the values can be different from bars 1-3
}

As an aside, I've been programming for about 10 years, profressionally for 3, and I have NEVER encountered a situation where I needed to do this. It is extremely likely that your problem could be solved without binding properties in groups of objects. What is your use case?