使用C#中的'ref'关键字与C++的比较

10

我主要使用C++进行工作,现在在我的新工作中使用C#。在这里阅读了一些关于'ref'关键字和c#值类型与引用类型的内容后,我仍然对它们感到困惑。

就我所理解的,如果将它们传递给方法,则它们类似于C ++样式:

值类型:

public void CSharpFunc(value)
and
public void CPlusplusFunc(value)

引用类型:

public void CSharpFunc(reference)
and
public void CPlusPlusFunc(&reference)

'ref' / 指针

public void CSharpFunc(ref bar)

public void CPlusPlus(*bar)

这个比喻正确吗?


在我看来,ref/pointer比喻并不好。在C#中(当然是托管的C#),根本没有指针的概念。 - ken2k
@ken2k,在C#中,“ref int a”与c/c++中的“int *a”具有相同的功能,它允许您在函数内部更改外部变量的值。 - weston
@dvvrd,那是不正确的。请看我的例子。 - Jaime Torres
@weston 不正确。请参考Konrad Rudolph的答案。此外,如果值的类型是引用类型,您可以在函数内修改“外部变量的值”而无需使用ref。 - ken2k
@weston:在C和C++中,指针更类似于C#中的对象,而不是ref。使用C或C++指针以及C#对象,您可以修改指针/对象所指向的位置的数据,但无法覆盖调用者对指针的副本。 - Sebastian Mach
显示剩余2条评论
3个回答

12

这个类比的说法正确吗?

尽管其他回答可能会认为是,但实际上不是。在 C++ 中,ref 的含义实际上取决于类型。对于值类型,你的评估是正确的(或足够接近)。而对于引用类型,一个更合适的类比应该是指针的引用:

public void CPlusPlus(type*& bar)
< p >关于< code >ref 的整个意义在于您可以更改传递的引用。而通过简单地传递指针,您无法在C ++中实现这一点:
void f(type* bar) {
    bar = new_address;
}

type* x;
f(x);

这段代码不会改变调用者的值 x。但如果你将 bar 作为 type*& 传递,它就会更改值。这就是 ref 的作用。

此外,在 C# 中,引用与 C++ 中的引用截然不同,更像是 C++ 中的指针(因为你可以更改引用所指向的对象)。


еҰӮжһңtype*жҳҜзұ»еһӢTпјҢйӮЈд№Ҳrefзӯүж•ҲеҮҪж•°йңҖиҰҒе…·жңүеҸӮж•°зұ»еһӢдёәT*жҲ–type**гҖӮжӮЁзҡ„зӨәдҫӢжҳҫзӨәдәҶдёҖдёӘе…·жңүзәҜзұ»еһӢTеҸӮж•°зҡ„еҮҪж•°гҖӮ - weston
@weston 好吧,这也足够公平。但是在C++中,大多数人会使用*&来修改指针参数,而不是**(后者在C中经常使用)。不幸的是,OP在他的问题中完全省略了类型,使一切都变得不清楚。 - Konrad Rudolph
谢谢,对不起我漏掉了 *& 和 **,我本应该包括它们的,但还是感谢您能够解读我的示例 :) - ElmsPlusPlus

3

更准确的说(虽然仍然不是完全一样),应该交换您的“引用类型”和“ref”示例。

在C#中,引用类型是一种始终通过对类型实例的引用进行内部访问的类型。最容易将这些视为C++意义上的“指针”:您分配内存,运行构造函数,并获得一个值,该值间接引用您想要的对象。 C#与C ++之间的区别在于,在C#中,这是类型的属性,而不是变量的属性。类型要么始终是引用类型,要么始终是值类型。这的一个影响是,您无需执行任何特殊操作即可使用引用类型(在托管C#代码中没有“解引用”运算符);编译器假定引用类型变量访问是间接的。在C ++中,您仍需要使用->运算符,因为您可以拥有相同类型的值和引用变量(object x vs. object *x)。

ref关键字用于按引用传递参数;这些参数可以是值类型(如int)或引用类型。虽然ref关键字的实现最终是地址/指针类型操作(正如C ++中的&一样),但ref(和out)生成一种称为托管引用的特殊对象,它不同于引用类型,因为您可以将托管引用传递给值类型。这几乎与C ++的工作方式完全相同:一个int&是一种特殊类型的“对int的引用”,它与int *不同,即使两者基本上都使用指针间接访问变量。类似地,在C#中,您可以拥有ref Object,这实际上是一个object *&


1

是的,你大部分是正确的,(s/*/*&和你百分之百正确)。正如@weston所提到的,out是另一个需要熟悉的关键字。你唯一可以用ref做的可爱的事情就是重载一个不是ref的函数。

class Person {
   string Name { get; set; }
   string Address { get; set; }
   int age { get; set; }
}

public void UpdateName(Person p)
{
   if (p == null) 
   {
      return;
   }

   p.Name = "Tom";
}

public void UpdateName(ref Person p)
{
   if (p == null)
   {
      p = new Person();
   }

   p.Name = "Tom";
}

显然是无用的,但它确实提供了一些有趣的机会(和糟糕的设计)。out不提供相同的重载功能。

如果您想要1对1,您可以始终使用unsafe来阻止您的代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接