字体不可变会使程序员和GC都感到困扰,因为每次都需要创建一个新实例。
为什么Font是一个不可变的引用类型呢?
答案 0 :(得分:29)
它简化了渲染系统的使用。
如果框架允许Font是可变的,则需要检测更改,并重新定义如何定期渲染。由于Font创建了一个本机资源,因此保持这种不可变性可以防止系统担心必须在内部重复重新创建句柄。
另外,我不同意“程序员的痛苦”。通过使Font不可变,可以更清楚地了解用户创建Font对象时发生的情况。如果需要新的Font,则需要创建一个新的Font对象,该对象又创建新的本机字体资源。使字体不可变使得更清楚的是发生了什么 - 你不太可能意外地产生性能问题。
如果字体是可变的,那么在更改字体属性时重复创建句柄就不那么明显了。
答案 1 :(得分:14)
好吧,问问自己一些问题。
首先,一个字体逻辑上是一个可变的东西,比如购物清单,还是一个不可变的东西,比如一个数字?如果您正在为程序中的购物清单建模,那么将其变为可变是有意义的,因为您通常会想到有一个购物清单,其中的内容会在您用完或购买特定商品时发生变化。但是你通常建模的数字是不可变的 - 数字12是数字12,现在和永远。
我认为“Helvetica 12点大胆”是一个固定的,不可改变的东西,就像一个数字,而不是我可以改变的东西。
其次,字体逻辑更像是一个可以复制的值,或者它更像是一个你可以参考的东西?我不认为有两份Helvetica;我想提到Helvetica。虽然数字我认为有不同的副本用于不同的目的 - 当我的购物清单上有12个项目和我的钥匙圈上有12个钥匙时,我不认为这两个东西都是“指12”。
由于我认为字体是不可变的和引用的,而不是像值一样可变和复制,我个人会将字体模型化为不可变引用类型。也许你对字体的直觉与我的不同。
答案 2 :(得分:8)
它们不是结构,因为它们需要有终结器来包装底层对象并提供合理的IDisposable
实现。如果您Dispose()
拥有自己struct
的副本会怎样?你每次都克隆句柄吗?
对GC的压力并不大......
它还允许Font
安全地重复使用,而不用担心它会在操作中途改变;-p
答案 3 :(得分:4)
我不同意这给程序员带来的困扰。 BCL中有很多不可变类型,程序员每天都会使用它们,不会引起任何问题。例如System.String。
不可变的一个好处是您不必每次都创建一个新实例。您可以随意重复使用相同的Font类型,因为它不会改变。另一方面,如果它是可变的,你需要每次都复制一份,以帮助保证没有其他人从你身下改变它。
最后,Font最严格意义上并不是一个不可变的类。它实现了IDisposable,并在Dispose方法中删除了底层的本机对象。
答案 4 :(得分:0)
你可能会说它让开发人员感到困扰。但是你也可以在相反的情况下提出相同的论点。
例如:
// 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
是可变的,上面的代码会将按钮和文本框的字体同时设置为粗体,这与开发人员的期望相反。
答案 5 :(得分:0)
Font是一个设计不佳的对象,违反了单一责任原则,您引用的困难源于此。 Font的问题在于它包含两件事:(1)如何绘制字体的描述,以及(2)具有这些属性的字体的GDI字体对象。前一种类型可能是可变的而没有后果,而后一种类型可变会带来各种各样的问题。
除其他外,考虑一个人应该如何跟踪典型控件(例如Button)字体属性的所有权的问题?如果有时会更改与控件关联的字体,应该为每个控件创建一个单独的Font对象,并在将控件的字体更改为其他内容时将其置于其中,或者应该保留一个正在使用的所有不同字体的列表为了避免创建过多的相同Font对象,或者是什么?
如果存在FontDescription结构(它是可变的,而不是IDisposable),并且像Control.Font这样的东西是FontDescription类型(或者更好的是,Control公开了一个带有FontDescription类型参数的SetFont方法),上面的问题可能是很简单地回答。实际上,设置控件字体的最有效方法是创建一个新的字体对象(如果还没有合适的字体对象),立即处理它,然后执行赋值。即使在Disposal之后,Font的“字体描述”部分仍然是准有效的,而这正是Control.Font属性真正需要的。