什么是可变类。我们如何在 C# 中创建可变和不可变类

2023-11-29

在 CMMI 5 级公司面试中,我被问及如何在 C# 中创建可变和不可变类。我听说过可变和不可变这意味着可以换 and 不能变, like String and 字符串生成器.

但是,我不知道如何创建可变类和不可变类。我们为此提供了字符串和字符串构建器。但是当谈到创建一个时,我被迫在网上搜索此内容,但找不到任何有用的东西,所以想到在这里询问。

但是,我尝试通过在类中定义一个属性来创建它,并在其 Getter 上创建一个新的字符串对象来复制它。但未能成功理解。

另外,我还提到 stackoverflow 中已经提出了一个关于不可变和可变的问题。但是,我的问题不同。我想知道如果我想创建一个可变类,那么除了使用 String 或其他可变类之外,我将如何使用它。


UPDATE:

截至 2020 年底,.NET 5.0 的 C# 9.0 已发布,支持不可变记录类型(支持复制构造函数,并支持with运算符轻松创建具有新属性值的新实例)。

原始答案,在 C# 9.0(和 8.0)发布之前编写:

C# 没有相同级别的支持const-C++ 提供的正确性(忽略代码契约),但它仍然提供readonly修饰符(以及 C# 6.0 中真正的只读自动属性)有帮助。

C# 还缺乏对 Record 类型的语法支持,不幸的是,它是从 C# 7 中删除的,所以我们必须再等一年(Update:截至 2018 年中期,C# 8.0 预计将拥有 Record 类型,但考虑到 C# 8.0 可能要到 2020 年才会最终发布它有很长的新功能列表).

Anyway, an immutable type in .NET is just a POCO1 which cannot have its state modified after construction. Note that this is only enforced by the compiler if your type has every field tagged as readonly and that every complex (i.e. non-scalar) member is also similarly constrained.

如果您的类型具有任何数组成员,则该类型不可能是真正不可变的,因为 C# 中没有强制执行只读缓冲区(C++ 中有)。这意味着在实践中,C# 中的“不可变”类型只是一个精心设计的 POCO,消费者(将遵守规则(例如无反射))可以在使用它时做出某些假设,例如不可变类型本质上是线程安全的。但仅此而已。运行时没有特殊的 AOT 或 JIT 优化,也没有表现出任何特殊的行为。这是一个非常“人为因素”的事情。

下面这个类是不可变的:

class Immutable {
    private readonly String foo;

    public Immutable(String foo, String bar) {
        this.foo = foo;
        this.Bar = bar;
    }

    public String Bar { get: }

    public String Baz { get { return this.foo.Substring( 0, 2 ); } }
}

它是不可变的,因为每个字段(即它的实例状态)都是readonly并且不可变(我们知道这一点只是因为System.String众所周知是不可变的)。如果foo被改为StringBuilder or XmlElement那么它就不再是一成不变的了。

请注意,严格来说,readonly修饰符对于不变性来说并不是必需的,它只是使它更容易演示,并且它确实增加了一定程度的编译时强制(并且possibly一些运行时优化)。

为了比较,这个类不是不可变的(即它是可变的):

class Mutable {
    private readonly Int32[] values;
    public Mutable(Int32 values) {
        this.values = values;
    }

    public Int32[] GetValues() {
        return this.values;
    }
}

它是可变的,因为:

  1. Int32[](数组类型)是可变的
  2. 它通过以下方式返回对可变数组的引用GetValues
  3. 它在构造期间接受可变对象参数。

这是一个示例,说明了为什么它不是不可变的:

Int32[] values = { 0, 1, 2, 3 };
Mutable mutable = new Mutable( values );

Print( mutable.GetValues() ); // prints "0, 1, 2, 3"

values[0] = 5;

Print( mutable.GetValues() ); // prints "5, 1, 2, 3"

If Mutable是不可变的,然后进行后续更改values使用时将不可见Mutable的API:第二次调用Print将显示与第一个相同的输出。

但是,即使您使用数组或复杂类型,您也可以拥有不可变类型:这是通过隐藏所有修改状态的方法来完成的。例如,返回ReadOnlyCollection<Int32>代替Int32[]并始终对传入的所有复杂和可变值执行深度复制/克隆。但是编译器、JIT 和运行时仍然不够复杂,无法确定这会导致对象类型不可变 - 因此为什么您必须记录它并相信您的消费者能够正确使用它(或者如果您是消费者,请相信您的上游开发人员)他们正确实施了)

下面是包含数组的不可变类型的示例:

class Immutable {
    private readonly Int32[] values;
    public Mutable(Int32 values) {
        if( values == null ) throw new ArgumentNullException(nameof(values)); 
        this.values = (Int32[])values.Clone();
    }

    public IReadOnlyList<Int32> GetValues() {
        return this.values;
    }
}
  • The input array is shallow-copied (using Array.Clone()) during construction - so any future changes to the object passed into the constructor won't affect any Immutable class instances.
    • If the values数组包含非不可变、非标量值,那么构造函数必须执行元素的“深层复制”,以确保其值与其他地方的任何未来更改隔离。
  • The valuesarray 永远不会直接暴露给消费者。
  • GetValues()返回一个IReadOnlyList<T> view内部数组的(这是 .NET 4.5 中的新功能)。这比返回一个更轻量级ReadOnlyCollection<T>包装器(在 .NET 2.0 中引入)。

1: A POCO is a "Plain Old CLR Object" type, which in-practice means any class or struct without any requirements that it inherit some parent supertype or implement any particular interface. The term is often used in reference to ORM libraries like Linq-to-SQL, Entity Framework or NHibernate which (in their earlier versions) required each entity class to derive from some base entity type, or employ certain techniques (e.g. INotifyPropertyChanged). See here for more details: What is POCO in Entity Framework?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是可变类。我们如何在 C# 中创建可变和不可变类 的相关文章

  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp
  • 如何在 C# 中将 ListView 与目录和文件绑定

    我想在 C 的 ListView 中绑定 C 驱动器中的所有目录和文件 我的代码是 protected void Page Load object sender EventArgs e DirectoryInfo di new Direct
  • 如何使用 JavaScript 中的值填充下拉列表?

    我在 Tridion CMS 扩展中的功能区工具栏按钮中添加了一个按钮 单击该按钮后 将显示一个弹出页面 其中包含两个下拉菜单 通过更改第一个下拉控件中的值 我应该填充第二个下拉控件的值 就我而言 我正在使用ASP drop down li
  • WPF 4.0 InvokeCommandAction 兼容性 (System.Windows.Interactivity)

    当我在 WPF 4 0 中插入 InvokeCommandAction Blend 4 SDK 时遇到问题 用户控件未显示 并且我遇到了一些例外情况 登录底部 如果我的 xaml 代码中没有该行 就没有问题 但没有事件触发器 我已经在外部灯
  • HttpClient 响应 ReadAsAsync() 未完全反序列化对象

    我正在尝试使用 Web API 客户端库来使用 Web 服务 我的问题是 当提交函数使用 POST 方法时 ReadAsAsync 似乎不想完全反序列化返回的对象 如果我得到字符串形式的响应并手动解串它就可以了 我收到一条 apmsgMes
  • SearchBoxControl 专注于在 Win8.1 上启动应用程序

    我在 Windows 8 1 应用程序中使用新的 SearchBox 控件 但每次启动应用程序时 SearchBox 都会获得焦点并显示搜索历史记录 我尝试将焦点设置在页面上的另一个控件上 但它不起作用 那么如何在不显示搜索历史记录的情况下
  • 演示如何在 C# 4.0 中使用新的“dynamic”关键字

    这是 4 0 版本中新的 C 未来 称为动态 告诉我如何在我的代码中使用它以及这个未来可以如何帮助我 相关问题 新的 dynamic C 4 0 关键字是否弃用了 var 关键字 https stackoverflow com questi
  • 扫描目录“User\Documents\My Music”时出现 UnauthorizedAccessException

    问题 为什么我在扫描用户的 我的文档 文件夹时出现此错误 但在扫描 我的音乐 我的图片 我的视频 目录时却没有出现此错误 次要 不太重要的问题 我正在和一位朋友谈论这个问题 他不懂技术 但了解足够的技术来进行对话 他帮助我进一步缩小了这个问
  • 可选参数代码在 .NET 3.5 中编译。为什么?

    这段代码在 VS 2010 的框架 3 5 项目中编译正常 我三次检查过 public LoggingClient string uri net msmq localhost logging 为什么 我在 C 4 规范中没有看到任何内容 文
  • 如何将表达式编译为实际结果?

    我正在使用表达式围绕 Web 服务调用构建一个 API 以允许开发人员指定查询并让 ExpressionVisitor 将表达式转换为查询字符串 该请求是 XML 具有包含查询字符串的特定元素 例如 我可以执行类似这样的操作 它将检索银行名
  • C# 数组如何存储在内存中

    我想我的主要问题是 只要我不重新初始化 新字节 作为参数传递的数组 这总是有效吗 static unsafe decimal GetDecimal byte ba decimal decimal PTR fixed byte byte PT
  • 在 C# 中替换部分文件名

    我有一个文件夹 pdf文件 在大多数文件的名称中 我想用另一个字符串替换特定字符串 这是我写的 private void btnGetFiles Click object sender EventArgs e string dir tbGe
  • 如何实现快捷键的键盘处理程序cefSharp

    我正在构建一个用于浏览网页的 Windows 应用程序cefSharp 我需要在该应用程序中实现一些快捷键 任何人都可以告诉我如何实现此功能 Ex ctrl tab move to next tab 我能够跟踪用户是否按下任何单个键 但无法
  • C# 动态/expando 对象的深度/嵌套/递归合并

    我需要在 C 中 合并 2 个动态对象 我在 stackexchange 上找到的所有内容仅涵盖非递归合并 但我正在寻找能够进行递归或深度合并的东西 非常类似于jQuery 的 extend obj1 obj2 http api jquer
  • 将多个表映射到实体框架中的单个实体类

    我正在开发一个旧数据库 该数据库有 2 个具有 1 1 关系的表 目前 我为每个定义的表定义了一种类型 1Test 1Result 我想将这些特定的表合并到一个类中 当前的类型如下所示 public class Result public
  • 在调试或发布控制台应用程序中创建文件夹

    我在 vs2010 C 中有一个控制台应用程序 在项目中 我添加了一个文件夹 右键单击项目 添加 gt 文件夹 我希望在编译应用程序 调试或发布 时 然后该文件夹将在调试或发布目录中创建 如果不存在 那可能吗 控制台应用程序是一个守护程序
  • 是否可以使用自定义类型属性的内容创建果园自动路线?

    我有一个 Orchard cms 模块 设置了一些附加的内容类型 并通过代码添加了一个 AutoRoute 组件 一切都很完美 但是我对默认的永久链接模式不满意 我想要做的是添加自定义模式并使用我的内容类型中的公共属性之一 就我而言 自定义
  • 使用 WPF 和数据绑定将文件拖放到应用程序窗口中

    我希望能够将文件 例如从桌面或资源管理器 拖放到 WPF 应用程序的主窗口中 我也不希望有任何代码隐藏 即我想使用数据绑定 到目前为止 我测试了 gong wpf dragdrop 它似乎不支持应用程序外部的拖动目标 我可以将文件拖放到主窗
  • LINQWhere(谓词)与FirstOrDefault(谓词)

    以下之间是否存在明显的性能差异 something Where predicate FirstOrDefault and something FirstOrDefault predicate 我倾向于同时使用两者 但我想知道在性能方面是否有
  • 在 MVC3 中创建下拉列表

    我正在尝试创建一个下拉列表来显示自定义集合类中的所有值 例如 public class MyCustomClassCollection public List

随机推荐