Страницы

Поиск по вопросам

пятница, 31 января 2020 г.

Корректный способ защиты множества объектов от изменений

#c_sharp #net #многопоточность #ооп


Нужно написать приложение, которые моделирует некоторые физические процессы. Будет
написан некоторый класс Simulation, содержащий основные методы управления процессом
моделирования (например метод Step()). Все операции в конечном итоге будут производиться
над множеством (HashSet) объектов типа Atom. Над этим множеством после моделирования
надо производить различные действия, к классу Simulation не относящиеся (да и перед
моделированием это множество надо соорудить отдельно или откуда-то загрузить). В процессе
моделирования (то есть пока будет выполняться метод Step()) множество атомов желательно
как-то защитить от воздействия извне, т. е. запретить добавление / удаление атомов,
изменение их свойств.

Можно сделать HashSet полем класса Simulation, определив так же методы Add(Atom
atom), Remove(Atom atom) и т. п. А для защиты изменения в процессе моделирования использовать
if (locked) return.

public class Simulation
{
    private readonly HashSet _atoms = new HashSet();

    . . .

    private bool locked;     

    public bool Add(Atom atom)
    {
        if (locked) return false;
        if (atom == null) throw new ArgumentNullException(nameof(atom));
        return _atoms.Add(atom);
    }

    public bool Remove(Atom atom)
    {
        if (locked) return false;
        return _atoms.Remove(atom);
    }

    public void Step(float dt)
    {
        locked = true;

        . . .

        locked = false;
    }

    . . .

}


Но такой подход не позволяет защитить атомы от изменения во время моделирования если
ссылки на них есть где-то вне множества _atoms.

Подскажите, пожалуйста, как корректно разрешить данную проблему.



EDIT

Атом представляет собой следующий класс:

[Serializable]
public class Atom
{
    private static int _numberOfInstances;

    [NonSerialized] private readonly int _hashCode;

    public Atom()
    {
        _hashCode = Interlocked.Increment(ref _numberOfInstances);
    }

    [field: NonSerialized] internal Vector3 Acceleration { get; set; }
    [field: NonSerialized] internal Vector3 Displacement { get; set; }

    public Vector3 Position { get; set; }
    public Vector3 Velocity { get; set; }

    public override int GetHashCode()
    {
        return _hashCode;
    }
}


И защищать тут нужно свойства Position и Velocity.
    


Ответы

Ответ 1



Вариант 1. Точно так же, как Вы уже показали. Добавить поле locked в Atom и проверять его во всех сетерах свойств. Вариант 2. Склонировать набор атомов в новые объекты, на которые ни у кого нет ссылок, и работать с ними.

Ответ 2



Если атомы - простые структуры и не содержат внутри себя ничего изменяемого, то можно сделать интерфейс только для чтения public interface IReadonlyAtom { int Id {get;} string Name{get;} } Написать реализацию атома public class Atom : IReadonlyAtom { public int Id {get;private set;} public string Name {get;set;} public Atom(int id, string name = null) { Id = id; Name = name; } // не забудьте перегрузки equals и gethashcode // если будете делать hashset с атомами } И сделать симуляцию полным владельцем коллекции атомов. То есть внешний код никак не сможет менять атомы или влиять как то ещё на них напрямую. public class Simulation { private readonly HashSet _atoms = new HashSet(); public void AddAtom(int id, string name = null) { _atoms.Add(new Atom(id, name)); } // Наружу отдаем коллекцию только для чтения с атомами только для чтения public IReadOnlyCollection Atoms => _atoms.Cast().ToList().AsReadOnly(); } Этот вариант может пригодиться в том случае, если атомов будет настолько много, что клонирование их займет много времени \ памяти.

Комментариев нет:

Отправить комментарий