【问题标题】:Why is Font immutable?为什么字体不可变?
【发布时间】:2012-01-31 08:04:09
【问题描述】:

不可变的字体让程序员和 GC 都感到苦恼,因为您每次都需要创建一个新实例。

那么为什么 Font 是不可变的引用类型呢?

【问题讨论】:

  • 我同意像 Label1.Font = new Font(Label1.Font.Name, Label1.Font.Size + 2.0F, Label1.Font.Style, Label1.Font.Unit); 这样的东西从“做我想做的事”的角度来看似乎有点过头了。
  • 让信用卡号码不可变,这样任何人都可以随时更改他们的号码!!!这会带来什么乐趣。在您添加它的同时 - 让每个人的名字不可变,以便我们可以根据需要每 5 分钟更改一次。

标签: c# fonts immutability


【解决方案1】:

它简化了渲染系统的使用。

如果框架要允许 Font 可变,则需要检测更改,并定期修改其呈现方式。由于 Font 创建了本机资源,因此保持此不可变可防止系统担心必须重复在内部重新创建句柄。

另外,我不同意“程序员的痛苦”。通过使 Font 不可变,用户创建 Font 对象时发生的事情变得更加明显。如果你想要一个新的 Font,你需要创建一个新的 Font 对象,这反过来又会创建新的原生字体资源。使 Font 不可变可以更清楚地了解正在发生的事情 - 您不太可能意外造成性能问题。

如果 Font 是可变的,那么在更改 Font 属性时重复创建句柄就不太明显了。

【讨论】:

  • 更不用说使用托管字体对象的任何对象(例如窗口)都必须检测本机句柄何时更改并将新句柄与它自己的本机对象(例如 HWND)相关联.
  • 我一直认为复制和稍微更改现有的 Font 对象是不必要的痛苦。我从不认为他们会创建原生资源,所以 +1 以获得有见地的答案。
  • 太糟糕了,尽管他们没有创建任何仅以粗体返回新实例的 AsBold() 方法。
  • @Itay:你注意到constructor that changes a font's style了吗?
【解决方案2】:

好吧,问自己一些问题。

首先,字体逻辑上是可变的东西,比如购物清单,还是不可变的东西,比如数字?如果您在程序中对购物清单进行建模,则使其可变是有意义的,因为您通常会考虑拥有一个购物清单,当您用完或购买特定物品时,其内容会发生变化。但是您通常将数字建模为不可变的数字——数字 12 就是数字 12,现在和永远。

我认为“Helvetica 12 点粗体”是一个固定的、不可变的东西,就像一个数字,我无法改变。

其次,字体逻辑上更像是一个可以复制的值,还是更像是一个可以引用的单一事物?我不认为拥有 Helvetica 的“两个副本”。我想参考 Helvetica。而我认为数字有不同的副本用于不同的目的——当我的购物清单上有 12 件物品和钥匙圈上有 12 把钥匙时,我不认为这两个东西都是“指 12”。

由于我认为字体是不可变的和被引用的,而不是可变的和按值复制的,我个人会将字体建模为不可变的引用类型。也许你对字体的直觉和我不一样。

【讨论】:

  • 在我看来,你对待事物的理念不一致得令人恼火。但我可能错过了一些东西......首先,您正在将一般杂货清单与特定数字进行比较。你也可以说“西瓜、奶酪、面包”永远是“西瓜、奶酪和面包”,而价格等数字可能非常可变......。一个数字对我们来说似乎更原子人类,但购物清单是实体,就像数字是实体一样,它们根据上下文既可变又不可变。就像真的永远是真的一样,但灯可以打开或关闭...... [继续]
  • @Itay:当我的妻子要求我在购物清单中添加牛奶时,我不会破坏旧清单并生成添加了牛奶的新清单。该列表是可变的,因此使用可变类型对购物清单进行建模是有意义的。当然,在某些情况下,将列表建模为不可变是有意义的。
  • @Eric - 我认为这是一个无法回答的哲学问题。如果我们从“时间之外”的角度来看,那么可以说所有版本的列表都可以共存,按时间索引,并且您今天更改列表不会改变昨天的列表。对我而言,相关问题是“这个世界模型是否简化了手头问题的解决方案?”,这将取决于上下文。
  • @kvb:你在时间之外的柏拉图式的永恒世界里买你的杂货吗?那肯定会解决莴苣枯萎的问题。
  • @Itay:我认为这些东西在设计类时非常重要。我不需要你同意我的看法。如果这些比较对您没有帮助,请不要使用它们。您的文档编辑系统示例很好;如果我正在编写这样一个系统,我可能会将文档建模为可变对象,因为这就是用户对文档的看法。但是,我也可以将其建模为不可变对象,因为这将使我能够更轻松地构建丰富的撤消重做系统。这是我会仔细考虑的工程决策;双方各有利弊。
【解决方案3】:

它们不是结构,因为它们需要终结器来包装底层对象并提供合理的IDisposable 实现。如果您 Dispose() 您自己的 struct 副本会发生什么?每次都克隆句柄吗?

GC 压力并不大...

它还允许安全地重复使用Font,而不必担心它在操作中途改变;-p

【讨论】:

  • 如果有一个可变的 FontInfo 结构和一个不可变的 ReadyFont 类会很有帮助;只有后者需要处理。控件上的 Font 属性之类的东西将作为 FontInfo 结构而不是 ReadyFont 公开。
【解决方案4】:

我不同意这让程序员感到苦恼。 BCL 中有很多不可变的类型,程序员每天都在使用它们,不会引起任何问题。例如 System.String。

不可变的好处之一是您不必每次都创建一个新实例。您可以根据需要多次重复使用相同的字体类型,因为它不会改变。另一方面,如果它是可变的,则您需要每次都制作一份副本,以帮助确保没有其他人从您手下更改它。

最后,Font 实际上并不是严格意义上的不可变类。它实现了 IDisposable 并在 Dispose 方法中拆除了底层的本机对象。

【讨论】:

  • 这让我想起了我一直想知道的事情:谁“拥有”控件中使用的字体、图像和其他对象?如果我创建一个字体并将其分配给控件的 Font 属性,它是否会复制传入的字体,所以我应该在不再需要它时处理字体,或者我应该保留对字体的引用,直到我处理控件或为其分配不同的字体,以便我可以正确处理字体?我通常的做法是简单地放弃我分配给控件的对象并让 GC 处理它们,但这感觉很恶心。
  • @supercat 我也从来不明白谁拥有Font 的答案,我不相信有一个好的答案。我也有同样的放弃方式,让GC捡起来。
  • 我忘了我之前有没有说过,但是控件的Font属性似乎是对字体的属性进行采样,而不考虑Font是否被释放。可以 Dispose 一个字体对象,但仍将 Control.Font 属性设置为已处理的字体。
  • 我的意思是语法上的痛苦
【解决方案5】:

您可以争辩说这让开发人员感到苦恼。但你也可以在相反的情况下提出同样的论点。

例如:

// Let me just set the button to the same font as the textbox...
button.Font = textBox.Font;

// ...except that I want the button's font to be bold.
button.Font.Bold = true;

如果Font 是可变的,上面的代码会将按钮和文本框的字体同时设置为粗体,这与开发人员的预期相反。

【讨论】:

    【解决方案6】:

    字体是一个设计不佳的对象,它违反了单一职责原则,而您引用的困难源于此。 Font 的问题在于它包含两件事:(1) 对如何绘制字体的描述,以及 (2) 具有这些属性的字体的 GDI 字体对象。前一种类型可以无结果地可变,而使后一种类型可变会带来各种问题。

    除其他事项外,请考虑如何跟踪典型控件(例如按钮)字体属性的所有权的问题?如果有时会更改与控件关联的字体,是否应该为每个控件创建一个单独的 Font 对象,并在将控件的字体更改为其他内容时处理它,或者应该保留一个正在使用的所有不同字体的列表为了避免创建过多相同的 Font 对象,还是什么?

    如果存在一个 FontDescription 结构(它是可变的,而不是 IDisposable)并且像 Control.Font 是 FontDescription 类型(或者更好的是,Control 公开了一个带有 FontDescription 类型参数的 SetFont 方法),上述问题可能是回答得很简单。实际上,设置控件 Font 的最有效方法是创建一个新的字体对象(如果还没有合适的字体对象),立即 Dispose 它,然后进行分配。即使在 Disposal 之后,字体的“字体描述”部分仍然是准有效的,这就是 Control.Font 属性真正需要的全部内容。

    【讨论】:

      猜你喜欢
      • 2013-03-29
      • 1970-01-01
      • 1970-01-01
      • 2013-03-04
      • 2021-01-27
      • 2017-03-20
      • 1970-01-01
      • 1970-01-01
      • 2016-03-13
      相关资源
      最近更新 更多