You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中引用类型搭配私有Setter与公共Getter的行为疑问

关于C#中私有Setter与嵌套引用类型的修改权限问题

好问题!这可是C#里不少新手容易踩的坑,我给你把这个逻辑掰扯明白。

首先先确认你已经搞懂的部分:对于valueType这种值类型,{ get; private set; }确实能完美实现“仅Foo类内部可写”的需求。因为值类型的get返回的是原数据的副本,外部就算修改这个副本也影响不到Foo实例里的原数据,再加上set是私有,外部根本没法直接给valueType赋值,所以完全安全。

但轮到fooBar这种嵌套的引用类型,情况就不一样了——private set只限制了引用本身的赋值权限,而不是引用指向的对象的内部状态!

具体来说是这样的:

fooBar的private set意味着外部代码永远不能做这件事:

var foo = new Foo();
foo.fooBar = new Foo.Bar(); // 编译报错!因为set是私有,外部无法重新赋值这个引用

但如果你的Bar类里有公共的可写成员(比如公共属性、字段),外部通过get拿到fooBar的引用后,完全可以修改Bar对象的内部内容。举个实际的代码例子:

假设你的Foo和Bar是这样实现的:

public class Foo {
    public class Bar {
        // 公共可写属性
        public string InnerText { get; set; }
        // 公共字段
        public int Counter;
    }

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

    public Foo() {
        // 内部初始化fooBar引用
        fooBar = new Bar();
    }
}

那外部代码可以轻松修改Bar的内部:

var fooInstance = new Foo();
// 完全合法!修改Bar对象的内部属性
fooInstance.fooBar.InnerText = "我是外部修改的内容";
// 同样合法!修改Bar对象的公共字段
fooInstance.fooBar.Counter = 999;

为什么会这样?

这本质是C#中引用类型的特性:当你通过get获取fooBar时,拿到的是指向堆上Bar对象的引用地址,而不是对象本身的副本。private set只管你能不能换这个地址(也就是给fooBar重新赋值新的Bar对象),但管不了你通过这个地址去修改对象内部的东西——只要Bar的成员是外部可访问的,就能改。

怎么解决?

如果想让Bar对象的内部也不能被外部修改,你需要把Bar设计成不可变类型,或者限制它的成员访问权限:

  1. 把Bar的所有可写成员改成仅内部可写,或者只提供getter:
public class Bar {
    // 只读属性,只能在构造函数或内部赋值
    public string InnerText { get; }
    // 私有字段,配合内部方法修改
    private int _counter;
    // 只读的公共访问器
    public int Counter => _counter;

    // 构造函数初始化
    public Bar(string innerText) {
        InnerText = innerText;
    }

    // 只有Foo内部能调用的修改方法
    internal void IncreaseCounter() {
        _counter++;
    }
}
  1. 或者直接把Bar的成员都设为私有,只通过Foo类提供的方法来修改Bar的状态——相当于把Bar的修改权限完全收拢到Foo内部。

这样一来,外部拿到fooBar的引用后,只能读取内容,没法做任何修改,就和值类型的valueType一样安全了。

内容的提问来源于stack exchange,提问作者c-a

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:01:05