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