C# 在方法签名中与 C++ 的“¶m”等价的是什么?
What is the C# equivalent to C++’s “¶m” 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 世界,ValueTypes 和 Reference 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;
此处,mt1
和 mt2
变量都包含对同一对象的引用。当该对象在最后一行代码中发生变化时,您最终会得到两个变量,它们都引用一个 TheValue
属性 为 42.
的对象
所有声明为 class 的类型都是引用类型。一般而言,除了数字类型、枚举和布尔类型之外,您通常遇到的大多数(但不是全部)类型都是引用类型。
任何被声明为 delegate
或 event
的内容在幕后也是引用类型。 (在对原始问题的回答中,这是发布到...)有人提到 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 变量所引用的对象都会发生改变,这会导致在外部可见的副作用功能。也就是说,所有内容仍然按值传递。
通过引用传递参数
您的问题在这里得到解答
您可以通过两种方式通过引用传递参数,使用 out
或 ref
关键字。在函数调用之前不需要初始化 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.
嘿,在 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 世界,ValueTypes 和 Reference 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;
此处,mt1
和 mt2
变量都包含对同一对象的引用。当该对象在最后一行代码中发生变化时,您最终会得到两个变量,它们都引用一个 TheValue
属性 为 42.
所有声明为 class 的类型都是引用类型。一般而言,除了数字类型、枚举和布尔类型之外,您通常遇到的大多数(但不是全部)类型都是引用类型。
任何被声明为 delegate
或 event
的内容在幕后也是引用类型。 (在对原始问题的回答中,这是发布到...)有人提到 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 变量所引用的对象都会发生改变,这会导致在外部可见的副作用功能。也就是说,所有内容仍然按值传递。
通过引用传递参数
您的问题在这里得到解答
您可以通过两种方式通过引用传递参数,使用 out
或 ref
关键字。在函数调用之前不需要初始化 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.