【问题标题】:Examples of Immutable Types in .Net.Net 中不可变类型的示例
【发布时间】:2015-10-21 16:11:24
【问题描述】:

我们知道不可变的概念,但除了不可变类型之外还需要了解一些不可变类型

  • 字符串
  • 日期时间

还有吗?

【问题讨论】:

  • 大多数人只知道字符串,更不用说日期时间了。所以我正在创建一个问题来帮助我们的开发者社区。​​span>
  • Re:创建不可变类型列表以便于参考。好主意,但也许最好只有一个(社区 wiki)答案,每个人都可以扩展;否则我们最终会得到大量答案,其中大多数可能只列出框架中不可变类型的一小部分。

标签: c# .net vb.net immutability


【解决方案1】:

框架类库中的不可变类型列表如下。 (请随意扩展!)

System.…

  • 所有原始值类型:(注意:not all value types are immutable!)
    • ByteSByte
    • Int16UInt16
    • Int32UInt32
    • Int64UInt64
    • IntPtr
    • Single
    • Double
  • Decimal
  • 编译器创建的所有匿名类型(C# 中的new { ... },VB.NET 中的New With { ... } (错误有两个原因:这些类型不在 FCL 中,显然是 VB。 NET 类型是可变的。)
  • 所有枚举类型(enumEnum
  • 所有委托类型。 (请参阅this answer。虽然委托似乎是可变的(因为您可以执行 obj.PropertyChanged += callback 之类的操作,但实际上是 obj.PropertyChanged 引用被变异为指向新构造的委托实例;原始委托实例保持不变。)
  • DateTimeTimeSpan (在this answer中提到)DateTimeOffset
  • DBNull
  • Guid
  • Nullable<T>
  • String
  • .NET 4 引入的Tuple<…> 类型(在this answer 中提到)
  • Uri
  • Version
  • Void

System.Linq.…

  • Lookup<TKey, TElement>

【讨论】:

  • 只说“所有值类型”+非值类型异常,例如String、Tuple和Uri,不是更容易吗?
  • @DaveDoknjas:很难说这是否真的会更容易,因为例外列表并不像您想象的那么短。例如,many IEnumerator<> implementations are mutable value types
  • 但是值类型的列表非常大。
  • 您永远不知道以后会在框架中添加哪些值类型,因此仅列出异常似乎比显式列出已知类型更不安全;即使列表变得更长。无论如何,OP 想要一个类型名称列表。
  • 您缺少枚举和匿名类型。
【解决方案2】:

我不确定您是在寻找 .NET 中的公开不可变类型还是完全不可变的类型。此外,您只想处理 .NET 中的公共类型吗?更深层次的问题是定义什么构成了不可变性。做一个只有

的类
public readonly int[] Numbers;

让它不可变? Numbers 本身不能更改,但其内容可以。你明白了。

无论如何,您都可以以编程方式检查自己。对于更深层次的嵌套检查,您将需要递归(我不会在这里这样做)

加载您要检查的所有程序集,并执行类似(未测试)之类的操作

var immutables = AppDomain.CurrentDomain
                .GetAssemblies()
                .SelectMany(t => t.GetTypes())
                .Where(t => t
                           .GetProperties(your binding flags depending on your definition)
                           .All(p => !p.CanWrite) 
                         && t
                           .GetFields(your binding flags depending on your definition)
                           .All(f => f.IsInitOnly)
                .ToList();

即使这样也不足以找到集合类型的不变性。一些不可变的集合类型(虽然不是默认 .NET 核心的一部分)可以在这里找到:Immutable Collections


一些值得注意的不可变:

  • 一些类类型,如StringTuple、匿名类型
  • 大多数结构(值得注意的例外包括大多数枚举器)
  • 枚举
  • 代表
  • 这样的不可变集合类型

    ImmutableArray(预发布版)

    不可变字典

    不可变排序字典

    不可变哈希集

    不可变列表

    不可变队列

    不可变排序集

    不可变堆栈

【讨论】:

    【解决方案3】:

    TimeSpan,或现代类型家族TupleTuple 是不可变的,因为它的实现是为了支持 .NET 中的函数式语言 (F# f.e.)。

    当然,您可以根据需要使自己的类和结构可变或不可变。不可变类型(又名值对象)在多线程编程、函数式编程、清除代码中很有用。

    要使类或结构不可变,只需删除所有公共/受保护/内部设置器;并更好地使用 readonly 关键字声明所有字段。

    【讨论】:

    • 只是为了明确一点,仅仅因为值类型恰好是不可变的并不意味着所有不可变对象都是值类型......
    • 一个改变它的内部状态而不从外部观察到的类型可以是不可变的:例如,一个昂贵的属性被懒惰地评估一次,然后结果被缓存。不可变类型不一定意味着 readonly 字段。
    • @Claies,是的,当然。我的文本中的 Value object 是模式名称,而不是 .NET 值类型。 Eric Evansf.e. 描述的这种模式
    • @InBetween,你是对的。但是,这种可能性表明该类至少有两个职责。这违反了面向对象设计的one of the principles。所以,你可以这样做,但你不应该这样做。
    • @MarkShevchenko :) 写完评论后,我开始考虑更多,最后问了 SO,因为我不确定正确答案是什么。这种类型是不可变的吗?我看到你发现了这个问题:p 我倾向于认为它是。
    【解决方案4】:

    来自 mscorlib 的一些示例:

    • 所有基本类型
    • 枚举
    • decimal
    • (U)IntPtr
    • DateTime, DateTimeOffset, TimeSpan
    • KeyValuePair<,> - 相反,DictionaryEntry 不是一成不变的
    • Tuple<...>
    • (Multicast)Delegate - 与字符串类似,更改时总是返回一个新实例。
    • Guid
    • Version

    有趣的是,string 实际上并不是一成不变的;但是,我们可以将其视为“实际上不可变”的类型,这意味着字符串实例的内容不能通过公共方式更改(至少在安全上下文中)。但它有一个 wstrcpy 内部方法,StringBuilder 类使用它来操作字符串实例。

    【讨论】:

      【解决方案5】:

      使用 .NET 5 和新的 C# 9.0 版本,现在几乎每个对象都可以是不可变的(或在属性中包含不可变状态)

      在此处了解更多信息:https://martinstanik.com/2020/10/09/immutable-data-types-after-net-5-release/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-11
        • 1970-01-01
        • 2023-04-09
        • 2017-05-03
        • 2014-07-22
        • 2011-12-07
        相关资源
        最近更新 更多