C # - Использование частного сеттера с публичным геттером на ссылочных типах - PullRequest
0 голосов
/ 27 апреля 2018

Рассмотрим следующий пример короткого кода с общедоступным и частным установщиком:

public class Foo        
{
    public class Bar
    {
        ...
    }

    public Bar fooBar { get; private set; }
    public int valueType { get; private set; }
}

Я хочу убедиться, что члены класса могут быть записаны только из класса Foo. Это работает для типов значений, таких как valueType в примере выше. Но как ведут себя ссылочные типы, такие как вложенный класс fooBar?

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

1 Ответ

0 голосов
/ 27 апреля 2018

Если тип, на который вы ссылаетесь, является изменяемым, то да, любой, имеющий ссылку на «контейнер», может получить ссылку и затем изменить объект. Например:

using System;
using System.Collections.Generic;

class Container
{
    public List<string> List { get; private set; }
        = new List<string>();
}

class Program
{
    static void Main()
    {
        var container = new Container();
        var list = container.List;
        Console.WriteLine(list.Count); //0

        container.List.Add("foo");
        Console.WriteLine(list.Count); // 1        
    }
}

Здесь List<string> видоизменяется снаружи Container. Один из способов избежать этого - использовать только изменяемое представление для изменяемых данных:

using System;
using System.Collections.Generic;

class Container
{
    private readonly List<string> list = new List<string>();

    public IReadOnlyList<string> ListView { get; }

    public Container()
    {
        ListView = list.AsReadOnly();
    }

    public void AddItem(string item)
    {
        list.Add(item);
    }
}

class Program
{
    static void Main()
    {
        var container = new Container();
        Console.WriteLine(container.ListView.Count); //0

        // container.ListView.Add("foo"); // Compile-time error

        container.AddItem("foo");
        Console.WriteLine(container.ListView.Count); // 1        
    }
}

Обратите внимание, что вы не должны просто возвращать список непосредственно из свойства, даже если тип времени компиляции равен IReadOnlyList<T> - потому что тогда вызывающая сторона может просто привести обратно к List<T> и изменить его. List<T>.AsReadOnly() возвращает подлинно доступный только для чтения объект-обертку по списку, поэтому вызывающие абоненты действительно не смогут его изменить.

...