Нужно группу экземпляров отсылать на одно и тот же поле

Описание

Есть у меня некий класс Limiter:

class Limiter
{
    public uint Index = 0;
    public Limiter GetSublimiter()
    {
        Limiter sublimiter = new();
        return sublimiter;
    }
}

Как видите она может создать себе подобных через метод GetSublimiter. Мне надо сделать так, чтобы у каждого экземпляра, созданный через GetSublimiter Index отсылался на тот же Index что и у "родителя". То есть:

static void Main(string[] args)
{
    Limiter limiter1 = new(); // limiter1.Index: 0
    Limiter sublimiter11 = limiter1.GetSublimiter(); // limiter1.Index: 0, sublimiter11.Index: 0
    sublimiter11.Index++; // limiter1.Index: 1, sublimiter11.Index: 1

    Limiter limiter2 = new(); // limiter2.Index: 0, limiter1.Index: 1, sublimiter11.Index: 1
}

Обратите внимание что Index у limiter1 и limiter2 разные, так как они созданы через конструктор, а не GetSublimiter.

Вопрос

Собственно какие варианты есть?

Дополнительно

Пытался вот так:

class Limiter
{
    public ref uint Index = 0;
    public Limiter GetSublimiter()
    {
        Limiter sublimiter = new();
        sublimiter.Index = ref this.Index;
        return sublimiter;
    }
}

Получил:

A ref field can only be declared in a ref struct.


Ответы (3 шт):

Автор решения: rotabor

Распространённое решение для такой задачи - это свойства Owner и Parent.

Parent указывает на объект, в который вложен текущий объект, а Owner указывает на самый верхний объект в этой иерархии.

Через эти свойства вы можете получить доступ к свойствам вышестоящих объектов, например, к свойству Index.

Сделать такой механизм в определениях типов не получится.

Это может быть закодировано примерно так:

class Limiter
{
    public Limiter Owner { get; init; }
    public Limiter Parent { get; init; }
    uint _index = 0;
    public Limiter()
    {
        Owner = Parent = null;
    }
    private Limiter(Limiter limiter)
    {
        Owner = limiter.Owner; Parent = limiter;
    }
    public Limiter GetSublimiter() => new Limiter(this);
    public uint Index => Owner?.Index ?? _index;
}
→ Ссылка
Автор решения: CrazyElf

Ну может сохранять ссылку на родителя и дальше смотреть - если есть родитель, то обращаемся к индексу родителя, если нет родителя, то обращаемся к собственному индексу.

class Limiter
{
    private Limiter? _parent = null;
    private uint _index = 0;

    public Limiter(Limiter? parent = null)
    {
        _parent = parent;
    }

    public Limiter GetSublimiter()
    {
        Limiter sublimiter = new(this);
        return sublimiter;
    }

    private void SetIndex(uint value)
    {
        if (_parent is null)
        {
            _index = value;
        }
        else
        {
            _parent.Index = value;
        }
    }

    public uint Index
    {
        get => _parent?.Index ?? _index;
        set => SetIndex(value);
    }
}

→ Ссылка
Автор решения: Alexander Petrov

Сделать класс-обёртку для индекса.

class Limiter
{
    private IndexWrapper index;
    public Limiter()
    {
        index = new IndexWrapper();
    }
    public Limiter GetSublimiter()
    {
        Limiter sublimiter = new() { index = this.index };
        return sublimiter;
    }
    public uint Index
    {
        get { return index.Value; }
        set { index.Value = value; }
    }

    private class IndexWrapper
    {
        public uint Value;
    }
}
→ Ссылка