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

C#如何禁止外部修改只读属性内部公共字段且不影响原类型复用

问题陈述

现有如下自定义向量类:

namespace StackoverflowQuestion1
{
    public class MyVector
    {
        public float x;
        public float y;
        public float z;

        public MyVector(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }
}

定义了可移动对象的通用接口IMovable,代表对象位置可变更:

namespace StackoverflowQuestion1
{
    public interface IMovable
    {
        public string Name { get; }
        public MyVector Position { get; }
    }
}

Furniture(家具)类属于可移动对象,因此实现了上述接口:

namespace StackoverflowQuestion1
{
    public class Furniture : IMovable
    {
        public string Name { get; private set; }
        public MyVector Position { get; private set; }

        public Furniture(string name, float x, float y, float z)
        {
            this.Name = name;
            this.Position = new MyVector(x, y, z);
        }
    }
}

按照预期,外部无法访问Name属性的私有setter,也无法直接修改Position属性(其setter为私有)。但实际上外部可以直接访问Position的x/y/z公共字段并修改其值,如下代码所示:

using StackoverflowQuestion1;

class Program
{
    static void Main(string[] args)
    {
        Furniture F = new Furniture("Chair", 1f, 2f, 3f);
        F.Name = "Office chair"; // 符合预期,编译报错
        F.Position = new MyVector(5f, 6f, 7f); // 符合预期,编译报错
        F.Position.x = 5f; // 不符合预期,可正常修改
        F.Position.y = 6f; // 不符合预期,可正常修改
        F.Position.z = 7f; // 不符合预期,可正常修改
    }
}
提问

如何实现仅Furniture类的内部成员可修改自身位置,外部完全无法修改Furniture的位置值,同时不能将MyVector类的坐标字段设为私有,避免MyVector在其他场景下无法正常修改使用?

解决方案

下面提供两种符合要求的常用实现方案:

方案1:对外返回MyVector副本(兼容性最高,无需修改原有接口)

核心逻辑是Furniture内部私有存储真实的位置向量实例,对外暴露的Position属性每次返回全新的副本,外部修改副本完全不会影响内部的真实值,内部需要修改位置时直接操作私有字段即可。
修改后的Furniture代码如下:

namespace StackoverflowQuestion1
{
    public class Furniture : IMovable
    {
        public string Name { get; private set; }
        // 内部私有存储真实位置,外部不可访问
        private MyVector _position;
        // 对外暴露的Position每次返回副本
        public MyVector Position => new MyVector(_position.x, _position.y, _position.z);

        public Furniture(string name, float x, float y, float z)
        {
            this.Name = name;
            this._position = new MyVector(x, y, z);
        }

        // 示例:内部修改位置的方法
        public void MoveInternal(float offsetX, float offsetY, float offsetZ)
        {
            _position.x += offsetX;
            _position.y += offsetY;
            _position.z += offsetZ;
        }
    }
}
  • 优点:完全不需要修改原有MyVector、IMovable的定义,兼容所有现有业务代码,MyVector在其他场景仍可正常修改字段使用。
  • 缺点:每次访问Position都会生成新的对象实例,高频访问场景下会有轻微的GC开销。

方案2:新增只读向量接口(性能最优,无额外开销)

核心逻辑是新增仅暴露x/y/z读取能力的只读接口,让MyVector实现该接口,调整IMovable的Position返回只读接口,外部拿到只读接口无法修改字段,Furniture内部仍存储可写的MyVector实例自由修改。
实现步骤如下:

  1. 新增只读向量接口:
public interface IReadOnlyVector
{
    float X { get; }
    float Y { get; }
    float Z { get; }
}
  1. 修改MyVector实现该接口(不需要修改原有公共字段,不影响其他场景使用):
public class MyVector : IReadOnlyVector
{
    public float x;
    public float y;
    public float z;

    public MyVector(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    // 显式实现只读接口,不污染原有类的公共成员
    float IReadOnlyVector.X => x;
    float IReadOnlyVector.Y => y;
    float IReadOnlyVector.Z => z;
}
  1. 修改IMovable接口的Position返回类型:
public interface IMovable
{
    public string Name { get; }
    public IReadOnlyVector Position { get; }
}
  1. 修改Furniture实现:
public class Furniture : IMovable
{
    public string Name { get; private set; }
    // 内部存储可写的MyVector
    private MyVector _position;
    // 对外返回只读接口
    public IReadOnlyVector Position => _position;

    public Furniture(string name, float x, float y, float z)
    {
        this.Name = name;
        _position = new MyVector(x, y, z);
    }

    // 内部修改位置的示例方法
    public void MoveInternal(float offsetX, float offsetY, float offsetZ)
    {
        _position.x += offsetX;
        _position.y += offsetY;
        _position.z += offsetZ;
    }
}
  • 优点:无额外对象创建开销,外部拿到IReadOnlyVector实例完全无法修改内部值,MyVector原有公共字段保留,其他场景可正常使用。
  • 缺点:需要小幅调整原有IMovable接口的定义,已有的依赖IMovable的代码需要适配。

内容的提问来源于stack exchange,提问作者jkf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 09:45:01