C# 等效于方法签名中C++的"¶m"是什么?



嘿,在C++中;操作员签名,例如:

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

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

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

以下是文档中的一个示例:

void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

参数传递的语义在很大程度上受到中的两种类型的影响。NET世界、ValueTypes引用类型。在理解像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类型一样,它们的行为(装箱时)就好像它们是从对象基类派生的(它们的派生是通过System.ValueType进行的)。对象(又名System.object)和System.ValueType都是引用类型,即使从System.ValueType派生的未装箱类型是值类型(这里有点神奇)。

所有值类型都是密封的/最终的-不能对它们进行子类。你也不能为它们创建默认构造函数——它们附带了一个默认构造函数,可以将它们初始化为默认值。您可以创建额外的构造函数(不会隐藏内置的默认构造函数)。

所有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的对象。

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

任何被声明为delegateevent的内容都是隐藏的引用类型。(在对最初问题的回答中,这被发布到…)有人提到了interface。不存在纯粹作为接口类型化的对象。结构和类都可以被声明为实现接口——这不会改变它们的值/引用类型性质,但存储在类型化为接口的变量中的结构将被装箱。

构造函数行为的差异

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

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是一个类(引用类型),SPoint是结构(值类型)。

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

var sp = new SPoint (42.0, 3.14);

所发生的是构造函数运行并创建一个值。然后将该值复制到sp变量中(该变量的类型为SPoint,其大小足以容纳一个双浮点SPoint)。

如果我这样做:

var cp = new CPoint (42.0, 3.14);

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

参数传递

抱歉序言花了这么长时间。

除非另有规定,否则函数/方法的所有参数都是通过值传递的。但是,不要忘记引用类型的变量的值是一个引用。

因此,如果我有一个函数声明为(MyType是上面声明的类):

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参数必须初始化)。在函数中,在函数返回之前,必须初始化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属性值的不同对象。

对于引用类型,当您正常传递变量时,传递它的对象可能会发生变异(在函数返回后,这种变异是可见的)。如果通过引用传递引用,则引用本身可能会发生变化,并且在函数返回后,引用的值可能会有所不同。

最新更新