【问题标题】:Use of C# 'ref' keyword compared to C++与 C++ 相比,C# 'ref' 关键字的使用
【发布时间】:2012-08-13 13:23:39
【问题描述】:

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

据我所知,如果您将这些传递给方法,这些将是类似的 C++ 样式:

值类型:

public void CSharpFunc(value)

public void CPlusplusFunc(value)

参考类型:

public void CSharpFunc(reference)

public void CPlusPlusFunc(&reference)

'ref'/指针

public void CSharpFunc(ref bar)

public void CPlusPlus(*bar)

这是一个正确的类比吗?

【问题讨论】:

  • 引用/指针类比不是一个好的 IMO。 C# 中根本没有指针概念(我的意思是,当然是托管 C#)。
  • @ken2k 好的,但是在 C# 中,ref int a 提供了与 c/c++ 中的 int *a 相同的能力。它允许您更改函数内部的外部变量的值。
  • @dvvrd 这是不正确的。请看我的例子。
  • 至少对于 C++ 代码部分(我无法判断 C# 是否正确),如果您使用实际有效的 C++ 语法会很有用。
  • @weston 确实,ref intint * 都允许您模拟“按引用传递”,但与 C 不同的是,C++ 具有 real 按引用传递融入语言,那就是int &

标签: c# c++


【解决方案1】:

这是一个正确的类比吗?

尽管其他答案说,不。 ref 在 C++ 中的含义实际上取决于类型。对于值类型,您的评估是正确的(或足够接近)。对于引用类型,更合适的类比是引用指针:

public void CPlusPlus(type*& bar)

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 够了。但这变得更加令人困惑,因为在 C++ 中,大多数人会使用 *& 来修改指针参数,而不是 **(尽管后者在 C 中经常使用)。不幸的是,OP 完全忽略了他的问题中的类型,使一切都不清楚。
  • 谢谢,我很抱歉遗漏了 *& 和 **,我真的应该包括它们,但还是感谢你能够破译我的例子 :)
【解决方案2】:

交换“参考类型”和“ref”示例会更准确(尽管仍然不完全相同)。

在 C# 中,引用类型 是始终通过对该类型实例的引用在内部访问的类型。在 C++ 意义上,将这些作为“指针”是最简单的:分配内存,运行构造函数,并取回一个间接引用所需对象的值。这里 C# 和 C++ 的区别在于,在 C# 中,这是 type 的属性,而不是 variable 的属性。类型要么是always 引用类型,要么是always 值类型。这样做的一个效果是您不必执行任何特殊操作即可使用引用类型(托管 C# 代码中没有“取消引用”运算符);编译器假定引用类型变量访问是间接的。在 C++ 中,您仍然需要使用 -> 运算符,因为您可以同时拥有相同类型的值和引用变量(object xobject *x)。

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

【讨论】:

    【解决方案3】:

    是的,你基本上是正确的,(s/*/*& 你是 100% 的)。正如@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 阻止您的代码。

    【讨论】:

      猜你喜欢
      • 2010-12-31
      • 1970-01-01
      • 2010-10-28
      • 2011-06-16
      • 1970-01-01
      • 2011-11-24
      • 2010-12-02
      • 1970-01-01
      • 2019-07-26
      相关资源
      最近更新 更多