我想你可能在想,会创建几个 Cache
对象的副本。你只希望有一个副本被多个客户对象共享。嗯,在C#中,有一个非常简单的规则可以记住,以确定你的对象会创建多少个独立的副本。
如果对象的类型是使用 class
关键字声明的,那么只有一种方式可以创建它的新实例:使用 new
关键字。
这里有一些微小的例外情况:你可以调用创建对象的BCL方法,但重要的是这是显式的。你必须明确地要求它发生。语言不会自动复制 class
对象。
所以在你的例子中,你有一个名为Cache
的class
,因此你可以确定地传递类型为Cache
的变量,尽可能多地传递,不会创建更多的Cache
副本。所有具有该对象分配给它们的变量都将“指向”同一原始对象。这是因为Cache
变量不存储对象本身,而只存储内存中Cache
对象的位置。
与此相反,如果您声明了一个struct
类型而不是class
,那么当您声明该类型的变量时,变量本身必须足够大,以存储在struct
中声明的所有数据。每个变量都是单独的副本。每个参数都是单独的副本。
您可以通过添加ref
关键字来覆盖此行为,但在大多数程序中,这是一个相当不寻常的关键字。 out
关键字更常见,并且最好被视为一种为方法提供多个返回值的方法。
ref
对于一个class
类型的变量有什么影响?以你的例子为例:
public ObjectLoader(Cache cache) {
}
我可以像这样构建两个对象加载器:
Cache c = new Cache();
ObjectLoader a = new ObjectLoader(c),
ObjectLoader b = new ObjectLoader(c);
我们刚刚创建了多少个对象?只需数一下
new
关键字即可。现在,假设我们添加了
ref
关键字:
public ObjectLoader(ref Cache cache) {
_cache = cache;
cache = new Cache();
}
在那个构造函数内部,我创建了另一个缓存,并将其存储在我传递的参数中。因为它是一个ref
参数,我已经影响了调用者的变量!所以在调用代码中:
Cache c = new Cache();
ObjectLoader a = new ObjectLoader(ref c),
ObjectLoader b = new ObjectLoader(ref c);
现在我们有五种使用 new
的方式:上面片段中的三种,加上两个修改后的 ObjectLoader
构造函数的调用。每次调用 ObjectLoader
的构造函数时,我们都将其传递给 c
。我们必须放置 ref
关键字,这是非常好的,因为它让阅读代码的人知道发生了一些奇怪的事情。变量 c
在 ObjectLoader
的构造函数返回后指向不同的 Cache
。所以 b
的 ObjectLoader
最终存储一个指向不同的 Cache
的指针,而不是 a
!
不用说,这将是一种非常混乱的代码模式。如果我们不需要在调用站点放置 ref
关键字,情况将会更糟!