我刚才用C#做了一个Swap例程:
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;
}
ref
和out
关键字就像C#指针一样,不使用unsafe
代码吗?
答案 0 :(得分:10)
他们更有限。你可以在指针上说++,但不能在ref
或out
上说。
编辑评论中有些混乱,所以要绝对清楚:这里的重点是与指针的功能进行比较。您无法在ptr++
/ ref
上执行与out
相同的操作,即将其设置为内存中的相邻位置。这是真的(但在此无关紧要),你可以执行相当于(*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
到第一个字节可以访问所有内容;它显然是指向第一个字节的指针。
答案 1 :(得分:6)
C#中的引用参数可用于替换一个指针的使用,是的。但不是全部。
指针的另一个常见用途是作为迭代数组的手段。 Out / ref参数不能这样做,所以不,它们不是“与指针相同”。
答案 2 :(得分:3)
ref
和out
仅用于函数参数,以表示参数将通过引用而不是值传递。从这个意义上讲,是的,它们有点像C ++中的指针(实际上更像是引用)。请在this article中了解详情。
答案 3 :(得分:2)
实际上,我将它们与C ++引用而不是指针进行比较。在C ++和C中,指针是一个更通用的概念,引用将做你想要的。
当然,所有这些都是无形的指针。
答案 4 :(得分:1)
简短回答是(类似功能,但机制不完全相同)。
另请注意,如果您使用FxCop分析代码,则使用out
和ref
将导致“Microsoft.Design”错误“CA1045:DoNotPassTypesByReference。”
答案 5 :(得分:1)
使用 out 的好处是,您可以保证为该项分配一个值 - 如果没有,您将收到编译错误。
答案 6 :(得分: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都有拷贝构造函数,否则...将无法工作,即使这样,交换指针的效率也会低得多。