8

我刚刚在 C# 中做了一个交换例程,如下所示:

static void Swap(ref int x, ref int y)
{
    int temp = x;
    x = y;
    y = temp;
}

它的作用与此 C++ 代码的作用相同:

void swap(int *d1, int *d2)
{
    int temp=*d1;
    *d1=*d2;
    *d2=temp;
}

那么refout关键字是否类似于 C# 的指针而不使用unsafe代码?

4

7 回答 7

10

他们更受限制。您可以在指针上说 ++,但不能在refor上说out


编辑评论中有些混乱,所以要绝对清楚:这里的重点是与指针的功能进行比较。您不能执行与/相同ptr++的操作,即使其寻址内存中的相邻位置。确实(但在这里无关紧要)您可以执行等效的,但那是将其与values的功能进行比较,而不是指针。refout(*ptr)++


可以肯定的是,它们在内部只是指针,因为堆栈不会移动,并且 C# 是经过精心组织的,因此ref始终out引用堆栈的活动区域。


编辑再次绝对清楚(如果从下面的示例中还不清楚的话),这里的重点不是/ 只能ref指向out堆栈。就是它指向栈时,语言规则保证不会变成悬空指针。这种保证是必要的(并且在这里相关/有趣),因为堆栈只是根据方法调用退出丢弃信息,没有检查以确保任何引用者仍然存在。

相反,当ref/out引用 GC 堆中的对象时,这些对象能够在必要时保持活动就不足为奇了:GC 堆的设计目的是为了在其引用者所需的任何时间长度内保留对象,并且提供固定(参见下面的示例)以支持不能通过 GC 压缩移动对象的情况。


如果您曾经在不安全的代码中使用互操作,您会发现它ref与指针密切相关。例如,如果一个 COM 接口声明如下:

HRESULT Write(BYTE *pBuffer, UINT size);

互操作程序集会将其变成这样:

void Write(ref byte pBuffer, uint size);

你可以这样做来调用它(我相信 COM 互操作的东西负责固定数组):

byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);

换句话说,ref第一个字节可以让您访问所有内容;它显然是指向第一个字节的指针。

于 2009-07-22T18:51:00.503 回答
6

C# 中的引用参数可用于替换指针的一种用法,是的。但不是所有的。

指针的另一个常见用途是作为迭代数组的一种方式。Out/ref 参数不能这样做,所以不,它们不是“与指针相同”。

于 2009-07-22T18:50:28.820 回答
3

ref并且out仅与函数参数一起使用以表示参数将通过引用而不是值传递。从这个意义上说,是的,它们有点像 C++ 中的指针(实际上更像是引用)。在这篇文章中阅读更多关于它的信息。

于 2009-07-22T18:54:51.010 回答
2

实际上,我会将它们与 C++ 引用而不是指针进行比较。在 C++ 和 C 中,指针是一个更通用的概念,引用会做你想做的事。

当然,所有这些无疑都是幕后的指针。

于 2009-07-22T18:54:01.887 回答
1

简短的回答是肯定的(类似的功能,但不完全相同的机制)。附带说明一下,如果您使用 FxCop 来分析您的代码,使用outandref将导致“CA1045:DoNotPassTypesByReference”的“Microsoft.Design”错误。

于 2009-07-22T18:51:46.773 回答
1

使用out的好处是可以保证项目将被分配一个值——如果没有,你会得到一个编译错误。

于 2009-07-22T18:53:21.247 回答
1

虽然比较是在旁观者的眼中......我说不。'ref' 改变调用约定,但不改变参数的类型。在您的 C++ 示例中,d1 和 d2 的类型为 int*。在 C# 中,它们仍然是 Int32 的,只是碰巧通过引用而不是值传递。

顺便说一句,您的 C++ 代码并没有真正交换传统意义上的输入。像这样概括它:

template<typename T>
void swap(T *d1, T *d2)
{
    T temp = *d1;
    *d1 = *d2;
    *d2 = temp;
}

...除非所有类型 T 都具有复制构造函数,否则将不起作用,即使这样也比交换指针效率低得多。

于 2009-07-22T18:58:14.873 回答