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

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

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

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

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

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

1 ответ

Решение

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

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() возвращает по списку подлинно доступный только для чтения объект-обертку, поэтому вызывающие действительно не смогут его изменить.

Другие вопросы по тегам