C# 在方法签名中与 C++ 的“&param”等价的是什么?

What is the C# equivalent to C++’s “&param” in a method signature?

嘿,在 C++ 中,可以在签名中包含 &Operator,例如:

void Test(GameVector &vecInfo)
{
    GameVector = &vecInfo;
    ....
}

你能用 C# 做同样的事情吗?那会是什么样子?

如果您想通过引用传递变量,可以使用 ref 关键字。

这是来自 docs 的示例:

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

参数传递的语义受.h中的两种类型的影响很大。 NET 世界,ValueTypesReference Types。在你理解像ref.

这样的东西之前,你需要很好地理解它们的意思

这个答案是从另一个已经关闭的问题中复制过来的(这是我的答案)。问题有点不同,但答案非常相似。

背景

.NET 领域有两种对象,引用类型值类型。两者之间的主要区别在于分配的工作方式。

值类型

当您将值类型实例分配给变量时,值会被复制到变量中。基本数值类型(int、float、double 等)都是值类型。结果,在这段代码中:

decimal dec1 = 5.44m;
decimal dec2 = dec1;
dec1 = 3.1415m;

两个十进制变量(dec 和 dec2)都足够宽以容纳十进制值的数字。在每种情况下,都会复制该值。最后,dec1 == 3.145m 和 dec2 == 5.44m。

几乎所有值类型都声明为结构(是的,如果您可以访问 .NET 源代码,int 就是一个结构)。与所有 .NET 类型一样,它们的行为(当装箱时)就好像它们是从对象基础 class 派生的(它们的派生是通过 System.ValueType。对象(又名 System.Object)和 System.ValueType 是引用类型,即使从 System.ValueType 派生的未装箱类型是值类型(这里发生了一点魔法)。

所有值类型都是 sealed/final - 您不能子 class 它们。您也不能为它们创建默认构造函数 - 它们带有默认构造函数,将它们初始化为默认值。您可以创建额外的构造函数(不隐藏内置的默认构造函数)。

所有enums也是值类型。它们继承自 System.Enum 但它们是值类型并且行为与其他值类型基本相同。

一般来说,值类型应该设计成不可变的;并非所有都是。

引用类型

引用类型的变量包含引用,而不是值。也就是说,有时将它们视为持有一个值会有所帮助 - 只是该值是对托管堆上对象的引用。

当您分配给引用类型的变量时,您正在分配引用。例如:

public class MyType {
    public int TheValue { get; set; }
    // more properties, fields, methods...
}

MyType mt1 = new MyType() {TheValue = 5};
MyType mt2 = mt1;
mt1.TheValue = 42;

此处,mt1mt2 变量都包含对同一对象的引用。当该对象在最后一行代码中发生变化时,您最终会得到两个变量,它们都引用一个 TheValue 属性 为 42.

的对象

所有声明为 class 的类型都是引用类型。一般而言,除了数字类型、枚举和布尔类型之外,您通常遇到的大多数(但不是全部)类型都是引用类型。

任何被声明为 delegateevent 的内容在幕后也是引用类型。 (在对原始问题的回答中,这是发布到...)有人提到 interface。没有纯粹作为接口类型的对象这样的东西。结构和 classes 都可以声明为实现接口 - 它不会改变它们的 value/reference 类型性质,但是存储在类型为接口的变量中的结构将被装箱。

构造函数行为的差异

引用类型和值类型之间的另一个区别是 new 关键字在构造新对象时的含义。考虑这个 class 和这个结构:

public class CPoint {
    public float X { get; set; }
    public float Y { get; set; }
    public CPoint (float x, float y) {
        X = x;
        Y = y;
    }
}

public struct SPoint {
    public float X { get; set; }
    public float Y { get; set; }
    public CPoint (float x, float y) {
        X = x;
        Y = y;
    }
}

基本相同,只是CPoint是class(引用类型),SPoint是结构(值类型)

当您使用两个 float 构造函数创建 SPoint 的实例时(请记住,它会神奇地自动获得一个默认构造函数),如下所示:

var sp = new SPoint (42.0, 3.14);

发生的是构造函数运行并创建一个值。然后将该值复制到 sp 变量(它是 SPoint 类型并且足够大以容纳两个浮点数的 SPoint)。

如果我这样做:

var cp = new CPoint (42.0, 3.14);

发生了一些非常不同的事情。首先,在托管堆上分配足够大的内存来容纳一个 CPoint(即,足以容纳两个浮点数加上作为引用类型的对象的开销)。然后运行双浮点构造函数(并且该构造函数是唯一的构造函数 - 没有默认构造函数(额外的,程序员编写的构造函数隐藏了编译器生成的默认构造函数))。构造函数在托管堆上分配的内存中初始化新的 CPoint。最后,创建对该新创建对象的引用并将其复制到变量 cp.

参数传递

抱歉序言拖了这么久。

除非另有说明,functions/methods的所有参数均按值传递。但是,不要忘记引用类型变量的值是一个引用。

所以,如果我有一个函数声明为(MyType 是上面声明的 class):

public void MyFunction(decimal decValue, MyType myObject) {
    // some code goes here
}

和一些看起来像这样的代码:

decimal dec1 = 5.44m;
MyType mt1 = new MyType() {TheValue = 5};
MyFunction (dec1, mt1);

发生的事情是 dec1 的值被复制到函数参数 (decValue) 并可在 MyFunction 中使用。如果有人在函数内更改 decValue 的值,函数外不会发生副作用。

类似但不同的是,mt1的值被复制到方法参数myObject中。但是,该值是对驻留在托管堆上的 MyType 对象的引用。如果在该方法中,某些代码改变了该对象(比如:myObject.TheValue=666;),那么 mt1 和 myObject 变量所引用的对象都会发生改变,这会导致在外部可见的副作用功能。也就是说,所有内容仍然按值传递。

通过引用传递参数

您的问题在这里得到解答

您可以通过两种方式通过引用传递参数,使用 outref 关键字。在函数调用之前不需要初始化 out 参数(而 ref 参数必须是)。在函数内,必须在函数 returns 之前初始化 out 参数 - 可以初始化 ref 参数,但不需要。这个想法是 ref 参数期望传入和传出函数(通过引用)。但是 out 参数被简单地设计为一种将某些东西传递出函数的方法(通过引用)。

如果我声明一个像这样的函数:

public void MyByRefFunction(out decimal decValue, ref MyType myObject) {
    decValue = 25.624;    //decValue must be intialized - it's an out parameter
    myObject = new MyType (){TheValue = myObject.TheValue + 2};
}

然后我这样称呼它

decimal dec1;       //note that it's not initalized
MyType mt1 = new MyType() {TheValue = 5};
MyType mt2 = mt1;
MyByRefFunction (out dec1, ref mt1);

调用之后,dec1 将包含值 25.624;该值通过引用传递出函数。

通过引用传递引用类型变量更有趣。函数调用后,mt1 将不再引用 TheValue 等于 5 创建的对象,而是引用新创建的 TheValue 等于 5 + 2 的对象(函数内创建的对象)。现在,mt1 和 mt2 将引用具有不同 TheValue 属性 值的不同对象。

对于引用类型,当您正常传递变量时,传递给它的对象可能会发生变异(并且该变异在函数 returns 之后可见)。如果通过引用传递引用,引用本身可能会发生变异,引用的值在函数之后可能会不同 returns.