通过参考?
我仍然对通过引用感到困惑。
如果我有一个Cache对象,我想访问/可用于许多对象,我使用构造函数注入注入它。 我希望它影响我创建的单个缓存对象。 例如。
public class Cache { public void Remove(string fileToRemove) { ... } } public class ObjectLoader { private Cache _Cache; public ObjectLoader(Cache cache) { } public RemoveFromCacheFIleThatHasBeenDeletedOrSimilarOperation(string filename) { _Cache.Remove(fileName); } }
当我将Cache传递给ObjectLoader构造函数时,我应该使用ref吗?
不,在这种情况下你不需要使用ref关键字。
Cache是一个类,它是一个引用类型。 将引用传递给方法时,引用的副本(不是对象本身)将放入您的参数中。 方法的内部和外部引用都指向堆上的同一对象,并且使用一个对象的字段的修改将反映在另一个中。
在方法调用中添加ref会传入原始引用。 这在您将在调用方法中重新分配(即通过调用new
)引用指向的位置的情况下非常有用。
当您需要修改引用指向的内容时,请使用’ref’关键字。 将引用类型传递给方法时,它将按值传递,但该值是传递给方法的引用的副本 。 这意味着您可以更改引用对象的常规状态(即属性/字段),但如果您尝试更改对您的引用点的影响,则只会影响副本。
例如,给定这种方法……
private void Foo( MyClass obj ) { obj = new MyClass( ); obj.SomeProperty = true; }
我们可以传入参数,然后查看它是否受到影响:
MyClass test = new MyClass( ); test.SomeProperty = false; Foo( test ); Console.WriteLine( test.SomeProperty ); // prints "False"
现在,如果我们使用’ref’关键字定义了方法…
private void Foo( ref MyClass obj ) { obj = new MyClass( ); obj.SomeProperty = true; }
输出将为“True”,因为实际引用已传递给方法,而不是副本。 我们更改了该参考指向函数内部的内容,并且我们看到了这些更改的效果。
当您省略’ref’关键字时,您只是在堆上创建一个指向对象的新指针。 如果更改一个指针,则不会更改另一个指针。
…
那么,回答你的问题; 不,您不需要使用’ref’关键字来更改单个Cache对象传递给方法时的状态。
我想你想知道会创建多少个Cache
对象的副本。 您只希望多个客户端对象共享一个副本。 好吧,只要您想知道将创建多少单独的对象副本,就可以在C#中记住一个非常简单的规则。
如果使用
class
关键字声明对象的类型,那么只有一种方法可以创建它的新实例:使用new
关键字。
这有一些小的例外:您可以调用创建对象的BCL方法,但关键是它是显式的。 你必须特别要求它发生。 该语言不会自动复制class
对象。
因此,在您的示例中,您有一个名为Cache
的class
,因此您可以确切地知道可以根据需要传递Cache
类型的变量,并且不会创建更多的Cache
副本。 将该对象分配给它们的所有变量将“指向”同一原始对象。 这是因为Cache
变量不存储对象本身,而只存储Cache
对象在内存中的位置。
将此与您声明struct
类型而不是class
情况进行对比。 现在,当您声明该类型的变量时,变量本身必须足够大,以存储struct
声明的所有数据。 每个变量都是一个单独的副本。 每个参数都是一个单独的副本。
您可以通过添加ref
关键字来覆盖它,但在大多数程序中它是一个非常不寻常的关键字。 out
关键字更常见,最好将其视为为方法提供多个返回值的方法。
如果变量属于class
类型,那么ref
对变量有什么影响? 在你的例子中:
public ObjectLoader(Cache cache) { // do stuff with cache (store it?) }
我可以像这样构造两个对象加载器:
Cache c = new Cache(); ObjectLoader a = new ObjectLoader(c), ObjectLoader b = new ObjectLoader(c);
我们刚创建了多少个对象? 只需计算new
关键字。 现在,假设我们添加了ref
关键字:
public ObjectLoader(ref Cache cache) { _cache = cache; // store // do something very odd! cache = new Cache(); }
隐藏在该构造函数中,我创建了另一个缓存,并将其存储在我传递的参数中。 因为它是一个ref
参数,所以我影响了调用者的变量! 所以在调用代码中:
Cache c = new Cache(); ObjectLoader a = new ObjectLoader(ref c), ObjectLoader b = new ObjectLoader(ref c);
现在我们有五种new
用法:上面代码片段中的三种,以及对修改后的ObjectLoader
构造函数的两次调用。 每次ObjectLoader
的构造函数时,我们都会传递它。 我们必须放置ref
关键字,这是一件非常好的事情,因为它让阅读代码的人知道发生了奇怪的事情。 在ObjectLoader
的构造函数返回后,变量c
指向不同的Cache
。 所以b
的ObjectLoader
最终将指向不同Cache
的指针存储到a
!
毋庸置疑,对于代码而言,这将是一个非常混乱的模式。 如果我们不必将ref
关键字放在调用站点,那就更糟了!
即使在.NET框架中声明它们通过函数参数中的值传递,对象也会通过引用自动传递。
这是因为对象本身是引用类型,因此您可以修改对象的成员,即使您无法替换对象本身。
看到
http://msdn.microsoft.com/en-us/library/aa903253(VS.71).aspx