提问人:Andreas Grech 提问时间:6/7/2009 最后编辑:Peter MortensenAndreas Grech 更新时间:3/17/2020 访问量:50002
在 C# 中,“ref”对引用类型变量有什么用?
What is the use of "ref" for reference-type variables in C#?
问:
我知道,如果我将值类型(、等)作为参数(不带关键字)传递,则该变量的副本将传递给该方法,但是如果我使用关键字,则会传递对该变量的引用,而不是新变量。int
struct
ref
ref
但是对于引用类型,如类,即使没有关键字,引用也会传递给方法,而不是副本。那么,带有 reference-types 的关键字有什么用呢?ref
ref
举个例子:
var x = new Foo();
以下有什么区别?
void Bar(Foo y) {
y.Name = "2";
}
和
void Bar(ref Foo y) {
y.Name = "2";
}
答:
在某些情况下,您希望修改实际引用,而不是指向的对象:
void Swap<T>(ref T x, ref T y) {
T t = x;
x = y;
y = t;
}
var test = new[] { "0", "1" };
Swap(ref test[0], ref test[1]);
评论
您可以更改要使用的点:foo
y
Foo foo = new Foo("1");
void Bar(ref Foo y)
{
y = new Foo("2");
}
Bar(ref foo);
// foo.Name == "2"
评论
它允许您修改传入的引用。例如:
void Bar()
{
var y = new Foo();
Baz(ref y);
}
void Baz(ref Foo y)
{
y.Name = "2";
// Overwrite the reference
y = new Foo();
}
如果您不关心传入的引用,也可以使用 out:
void Bar()
{
var y = new Foo();
Baz(out y);
}
void Baz(out Foo y)
{
// Return a new reference
y = new Foo();
}
使用 ref 关键字传递引用类型时,将按引用传递引用,并且调用的方法可以为参数分配新值。该更改将传播到调用范围。如果没有 ref,则引用将按值传递,并且不会发生这种情况。
C# 也有 'out' 关键字,它与 ref 非常相似,只是使用 'ref' 时,必须在调用方法之前初始化参数,而使用 'out' 时,必须在接收方法中分配一个值。
另一堆代码
class O
{
public int prop = 0;
}
class Program
{
static void Main(string[] args)
{
O o1 = new O();
o1.prop = 1;
O o2 = new O();
o2.prop = 2;
o1modifier(o1);
o2modifier(ref o2);
Console.WriteLine("1 : " + o1.prop.ToString());
Console.WriteLine("2 : " + o2.prop.ToString());
Console.ReadLine();
}
static void o1modifier(O o)
{
o = new O();
o.prop = 3;
}
static void o2modifier(ref O o)
{
o = new O();
o.prop = 4;
}
}
Jon Skeet 写了一篇关于在 C# 中传递参数的好文章。它清楚地详细说明了按值、引用 () 和输出 () 传递参数的确切行为和用法。ref
out
以下是该页面中与参数相关的重要引述:ref
引用参数不传递 中使用的变量的值 函数成员调用 - 他们使用 变量本身。而不是 创建新的存储位置 函数成员中的变量 声明,相同的存储位置 ,所以变量的值 在函数成员和值中 的引用参数将始终 是一样的。参考参数需要 ref 修饰符作为两者的一部分 声明和调用 - 那 意味着当你 通过引用传递某些内容。
评论
这里解释得很好:http://msdn.microsoft.com/en-us/library/s6938f28.aspx
文章摘要:
引用类型的变量不直接包含其数据;它 包含对其数据的引用。传递引用类型时 参数,可以更改指向的数据 引用,例如类成员的值。但是,您 不能更改引用本身的值;也就是说,你不能 使用相同的引用为新类分配内存并拥有它 在块外保留。为此,请使用 ref 或 out 关键字。
评论
方法中的参数似乎总是传递一个副本,问题是什么的副本。复制是由对象的复制构造函数完成的,由于所有变量在 C# 中都是 Object,我相信所有这些变量都是这种情况。变量(对象)就像住在某些地址的人。我们要么改变居住在这些地址的人,要么我们可以在电话簿中创建更多居住在这些地址的人的参考(制作浅拷贝)。因此,多个标识符可以引用同一地址。引用类型需要更多的空间,因此与通过箭头直接连接到堆栈中的标识符的值类型不同,它们对堆中的另一个地址具有值(更大的空间)。需要从堆中获取此空间。
值类型: Indentifier(contains value =堆栈值的地址)---->值类型的值
参考类型: 标识符(包含值=堆栈值的地址)---->(包含值=堆值的地址)---->堆值(通常包含指向其他值的地址),想象一下,更多的箭头在不同的方向上伸向 Array[0]、Array[1]、array[2]
更改值的唯一方法是按照箭头操作。如果一个箭头丢失/更改,则无法访问该值。
除了现有的答案:
正如您询问的 2 种方法的区别: 使用 或 时没有协方差:ref
out
class Foo { }
class FooBar : Foo { }
static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }
void Main()
{
Foo foo = null;
Bar(foo); // OK
Bar(ref foo); // OK
FooBar fooBar = null;
Bar(fooBar); // OK (covariance)
Bar(ref fooBar); // compile time error
}
引用变量将地址从一个地方带到另一个地方,因此在任何地方对它们的任何更新都会反映在所有地方,那么 REF 有什么用。 引用变量 (405) 一直有效,直到没有为方法中传递的引用变量分配新内存。
一旦分配了新的内存(410),那么这个对象(408)上的值变化就不会反映在任何地方。 因为这个参考来了。Ref 是引用的引用,因此每当新内存分配它时,它都会知道,因为它指向该位置,因此该值可以由 everyOne 共享。您可以查看图像以获得更清晰的信息。
评论