C#-在引用类型上使用带有公共getter的私有setter

C#-在引用类型上使用带有公共getter的私有setter,c#,properties,encapsulation,getter-setter,reference-type,C#,Properties,Encapsulation,Getter Setter,Reference Type,考虑以下带有公共getter和私有setter的简短代码示例: public class Foo { public class Bar { ... } public Bar fooBar { get; private set; } public int valueType { get; private set; } } 我想确保只能从Foo类内部写入类成员。这适用于上面示例中的值类型,如valueType。但是像嵌套类f

考虑以下带有公共getter和私有setter的简短代码示例:

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

    public Bar fooBar { get; private set; }
    public int valueType { get; private set; }
}
我想确保只能从
Foo
类内部写入类成员。这适用于上面示例中的值类型,如
valueType
。但是像嵌套类
fooBar
这样的引用类型是如何工作的呢


我可以使用public getter返回的引用来操作我的私有成员吗?

如果您引用的类型是可变的,那么是的,任何对“容器”有引用的人都可以获取引用,然后修改对象。例如:

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        
    }
}

请注意,即使编译时类型是
IReadOnlyList
,也不应该直接从属性返回列表,因为调用方可以直接回溯到
list
并对其进行变异
List.AsReadOnly()
在列表周围返回一个真正的只读包装器对象,因此调用者确实无法对其进行修改。

您是对的,私有setter只会保护Foo的引用,而不是它引用的实际类实例。最好的方法是将
设置为Bar
。私有列表不必是只读的。属性返回list.AsReadOnly()就足够了;达到要求的方面。是吗?@Nina:如果
列表
字段不是只读的,那么它所指的列表可能会发生变化,此时,
ListView
属性将有效失效-它将继续引用旧列表周围的包装器。在本例中,只读修饰符只会阻止将完全不同的列表分配给列表字段,但不会阻止通过添加和删除项来修改原始列表。我做对了吗?@尼娜:是的,没错。
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        
    }
}