Как создать интерфейс для доступа к свойствам объекта в C#? - PullRequest
0 голосов
/ 28 апреля 2020

Я хочу обобщить ограниченный доступ к определенным полям объекта:

class Foo {
    public int Bar;
    public int Qux;
}

Для этого я объявил интерфейс:

interface IModifiableFoo {
    int Bar {get; set;}
}

Затем, чтобы ограничить доступ к этот объект, я создал класс, который оборачивает его:

class ModifiableFoo : IModifiableFoo {
    private readonly Foo ActualFoo;

    ModifiableFoo(ref Foo foo) {
        ActualFoo = foo;
    }

    public int Bar {
        get => ActualFoo.Bar;
        set { ActualFoo.Bar = value; }
    }
}

Наконец, я использую эту оболочку для предоставления ограниченного доступа к объекту Foo:

Foo myFoo = new Foo();

IModifiableFoo ModifyFoo() {
    return (IModifiableFoo) new ModifiableFoo(ref MyFoo);
}

IModifiableFoo fooAccess = ModifyFoo();
fooAccess.Bar = 3;

Console.WriteLine(myFoo.Bar); // 3

В результате контракт IModifiableFoo действует как обобщение того, какие поля можно изменить, когда вы получаете право изменять Foo.

Однако, поскольку интерфейс объявляет только получатель и установщик их фактическая реализация не ограничена и позволяет этому произойти:

class ModifiableFoo : IModifiableFoo {
    ModifiableFoo(ref Foo foo) {}

    public int Bar {
        get => 42,
        set {
            Console.WriteLine("Not something you would expect");
        }
    }
}

Как реализовать эту функцию и обеспечить, чтобы реализация всегда получала и задавала соответствующее поле?

Ответы [ 2 ]

0 голосов
/ 30 апреля 2020

Форма решения соответствует конкретной c проблеме. Из того, что я понимаю по вашему вопросу, вы хотите контролировать права на чтение / запись данных во время выполнения приложения. CvarRegistry приходит на ум. Пожалуйста, обратитесь к оригиналу от id Software, так как это всего лишь набросок.

    public Cvar { }
    public Cvar<T>
    {
        public T Value { get; set; }
    }
    public CvarRegistry : Dictionary<string, Cvar> { }

Вы можете динамически контролировать доступ путем: 1) добавления и удаления Cvars в доступную коллекцию (надеясь, что потребители этого не сделают держитесь за ссылки) 2) добавление версий только для получателя в доступную коллекцию, или 3) создание регулятора, который исключительно переключает состояние только для чтения каждого значения Cvar, что вызывает ветвление во время доступа сеттера.

К сожалению, Свойство объекта может быть целым объектом со своими собственными доступными свойствами, и поэтому утечка искусственного контроля. Реальная CvarRegistry плоская, так как каждый Cvar указывает на примитив. Я не нашел способа ограничить родовые типы примитивами, но я не считаю это необходимым.

OOP учит вас концептуализировать данные как личную собственность или состояние объекта. Осознание этого предположения вынуждает вас затем вручную доставлять значения «состояния» между «объектами». Это приводит к форме процесса, известного как распределенное производство . Каждый модуль OOP имеет избыточное (внутреннее) представление (общей) областей памяти, которыми они управляют, и отправляет друг другу электронные сообщения. Напротив, эталонные свойства (Cvars) позволяют различным модулям просто совместно использовать память.

    public Cvar<int> x { get; set; }

    public void M()
    {
        x.Value += 3;
    }
    public void N() 
    {
        x.Value *= 4;
    }

    // N() could be a method in another class, another namespace
    // and you would have zero coupling

Моя производственная архитектура представляет собой целую альтернативную парадигму OOP, где модули взаимодействуют с конечным продуктом, а не искусственно владеют и управление памятью, как будто это всегда внутреннее состояние. Таким образом, другой способ взглянуть на проблему - спросить, нужны ли «свойства объекта». Иногда они есть, но не в производственном контексте, который является большей частью кода.

0 голосов
/ 28 апреля 2020

Если вы хотите гарантировать какую-то часть реализации, то мне кажется, что лучший способ контролировать это - реализовать ее. Вы знаете, что хотите предоставить метод, вы знаете, что ему нужно обернуть Foo, и вы знаете, что вы хотите, чтобы этот метод вызывал согласованное поведение вещей, так что, возможно, абстрактный класс?

    public abstract class ModifiableFoo : IModifiableFoo
    {
        private Foo _foo;

        protected ModifiableFoo(Foo foo)
        {
            _foo = foo;
        }

        public int Bar
        {
            get => _foo.Bar; 
            set { _foo.Bar = value; }
        }
    }

    public class ExtendedModifiableFoo : ModifiableFoo
    {
        public ExtendedModifiableFoo(Foo foo) : base(foo)
        {

        }

        public int Bar // this doesn't work as shown because Bar is not overridable
        {
            get => _foo.Bar; // nor does this because _foo is inaccessible due to its protection level
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...