译自Eric Lippert's Blog, 原文: http://blogs.msdn.com/ericlippert/archive/2009/08/27/what-s-the-difference-between-fixed-and-fixed.aspx
某天我收到一封这样开头的邮件:
我有一个关于C# 固定大小的缓冲区的问题:
unsafe struct FixedBuffer { public fixed int buffer[100]; }
现在把缓冲区声明为固定的,它就是不可移动的...
看到这个问题我的心都碎了。如果语言细节设计容易造成误解,那么上面所遇到的情况只是极为不幸的时刻之一。
当在非安全的代码中对托管对象使用指针算法时,你要确保垃圾回收器不会把你正需要的内存移掉。当你正在用指针操作某个对象的时候,如果另外一个线程进行了资源回收,那么这个指针将完全混乱。因此,C#把变量分为“固定的”和“可变的”。如果你想要将指针应用到一个可移动的对象上,你可以使用“fixed”关键字来声明“这个局部变量是不能被垃圾回收器移动的”。当回收行为发生时,垃圾回收器要为那些正在进行的调用查看所有局部变量(那些被用到的变量需要保留);如果回收器看到某变量被标为“fixed”,它不会移动这样的变量,即使这会造成托管堆碎片(由此也看出让这样的变量固定的时间尽可能少是很重要的)。所以典型地,我们用“fixed”代表固定在某个地方。
但是在这封邮件中的“fixed”并不是这个意思,这里的意思是将此问题中的buffer的大小固定为容纳100个int变量,本质上讲,这和在结构中创建100个int类型的成员是一样的。
明显地,我们经常使用相同的关键字来表示概念上相同的东西。比如,在C#中我们经常用不同的方式使用关键字“internal”,但是所有的“internal”表示的意义都是一样的。它只是用来表示“一些实体的访问权限在同一程序集的代码中是不受限制的”。
某些时候我们也用相同的关键字来表示概念上完全不同的东西,这要依赖于用户使用的上下文环境来确定它代表的意思。比如:
var results = from c in customers where c.City == "London" select c;
和
class C<T> where T : IComparable<T>
很明显“where”使用于两种完全不同的方式:建立查询语句中的筛选从句,和声明一个泛型参数的约束类型。
如果一个关键字用于两种不同的意义但是这个区别非常细微,就像我们上面举到的例子,这会使人们碰到困难。那个用户的邮件将会继续问上一大堆的问题,但是这些问题都是基于一个不正确的假设——一个固定大小的缓冲区会自动地固定在内存中的某一位置。
这只能说这是一个不好的术语的混用:“固定大小”和“固定位置”都是使用“fixed”,但是使用的方式不同,这是很让人头疼的事情。然而它们之间的关联比这还要复杂:只有当某固定大小的缓冲区的容器被固定在某块内存区,你才能安全地访问存储在该缓冲区中的数据。在这个问题中,这两个概念具有很强的相关性,但是并不完全相同。
一方面,为了减少混乱,我们可以使用两个不同的关键字,比如说“pinned”和“fixed”。但是另一方面,“fixed”的所有用法只在非安全代码中才可用。对于非安全的码的所有特性,一个关键的前提假设就是:如果你想在C#中使用非安全代码,那么你肯定已经是一个完全理解CLR中内存管理的编程专家。这也是为什么我们让你在代码中标明“unsafe”的原因;它表明你关闭了安全系统并且知道自己在做什么。
在C#中可以多用的一些重要的关键字:fixed, into, partial, out, in, new, delegate, where, using, class, struct, true, false, base, this, event, return和void都至少包含两种以上不同的意思。大部分的在位于特定上下文中的时候都很清楚,但是至少前三个——fixed, into和partial——已经造成了很多的困扰,我已经收到了很多困惑的用户关于它们使用区别的问题。我接下来会看一下“into”和“partial”。