简体   繁体   English

在方法签名中,C# 相当于 C++ 的“¶m”是什么?

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

Hey in C++ it is possible to have &Operator in Signature for example:嘿,在 C++ 中,可以在 Signature 中使用 &Operator,例如:

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

Are you able to do the same in C#?你能在 C# 中做同样的事情吗? How would that look like?那会是什么样子?

If you would like to pass a variable by reference, you can do so using the ref keyword.如果要通过引用传递变量,可以使用ref关键字。

Here's an example from the docs :这是文档中的一个示例:

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

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

The semantics of parameter passing are greatly influenced by the two kinds of types in the.参数传递的语义受 中的两种类型的影响很大。 NET world, ValueTypes and Reference Types .网络世界,值类型引用类型 You need to get a good understanding of what they mean before you can understand things like ref .在理解诸如ref东西之前,您需要很好地理解它们的含义。

This answer is copied from another question that has since closed (it is my answer).这个答案是从另一个已经关闭的问题中复制过来的(这是我的答案)。 The question was somewhat different, but the answers are very similar.问题有些不同,但答案非常相似。

Background背景

There are two kinds of objects in .NET-land, Reference types and Value types . .NET-land 中有两种对象,引用类型值类型 The main difference between the two is how assignment works.两者之间的主要区别在于分配的工作方式。

Value Types值类型

When you assign a value type instance to a variable, the value is copied to the variable.当您将值类型实例分配给变量时,该值将被复制到该变量中。 The basic numeric types (int, float, double, etc) are all value types.基本的数字类型(int、float、double 等)都是值类型。 As a result, in this code:结果,在这段代码中:

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

both decimal variables (dec and dec2) are wide enough to hold a decimal valued number.两个十进制变量(dec 和 dec2)都足够宽以容纳十进制数值。 In each case, the value is copied.在每种情况下,都会复制该值。 At the end, dec1 == 3.145m and dec2 == 5.44m.最后,dec1 == 3.145m 和 dec2 == 5.44m。

Nearly all value types are declared as a struct (yes, if you get access to the .NET sources, int is a struct).几乎所有值类型都声明为结构体(是的,如果您可以访问 .NET 源,则 int 是一个结构体)。 Like all .NET types, they act (when boxed) as if they are derived from the object base class (their derivation is through System.ValueType. Both object (aka System.Object) and System.ValueType are reference types, even though the unboxed types that derive from System.ValueType are value types (a little magic happens here).与所有 .NET 类型一样,它们的行为(当装箱时)就好像它们是从对象基类派生的(它们的派生是通过 System.ValueType。对象(又名 System.Object)和 System.ValueType 都是引用类型,即使派生自 System.ValueType 的未装箱类型是值类型(这里发生了一个小魔法)。

All value types are sealed/final - you can't sub-class them.所有值类型都是密封/最终的 - 你不能对它们进行子类化。 You also can't create a default constructor for them - they come with a default constructor that initializes them to their default value.您也不能为它们创建默认构造函数——它们带有一个默认构造函数,可以将它们初始化为默认值。 You can create additional constructors (which don't hide the built-in default constructor).您可以创建其他构造函数(不会隐藏内置的默认构造函数)。

All enums are value types as well.所有enums也是值类型。 They inherit from System.Enum but are value types and behave mostly like other value types.它们从 System.Enum 继承,但属于值类型,其行为大多与其他值类型相似。

In general, value types should be designed to be immutable;一般来说,值类型应该设计为不可变的; not all are.并非全部都是。

Reference Types引用类型

Variables of reference types hold references, not values.引用类型的变量保存引用,而不是值。 That said, it sometimes help to think of them holding a value - it's just that that value is a reference to an object on the managed heap.也就是说,考虑它们持有一个值有时会有所帮助 - 只是该值是对托管堆上对象的引用。

When you assign to a variable of reference type, you are assigning the reference.当您分配给引用类型的变量时,您正在分配引用。 For example:例如:

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

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

Here, the mt1 and mt2 variables both contain references to the same object.这里, mt1mt2变量都包含对同一对象的引用。 When that object is mutated in the final line of code, you end up with two variables both referring to an object whose TheValue property is 42.当该对象在最后一行代码中发生变异时,您最终会得到两个变量,它们都指向一个TheValue属性为 42 的对象。

All types declared as a class are reference types.所有声明为类的类型都是引用类型。 In general, other than the numeric types, enums and bools, most (but not all) of the types that you normally encounter will be reference types.通常,除了数字类型、枚举和布尔值之外,您通常遇到的大多数(但不是全部)类型都是引用类型。

Anything declared to be a delegate or an event are also reference types under the covers.任何声明为delegateevent的内容也是隐藏的引用类型。 (In an answer to the original question this was posted to...) Someone mentioned interface . (在对原始问题的回答中,这被发布到...)有人提到了interface There is no such thing as an object typed purely as an interface.没有纯粹作为接口类型化的对象。 Both structs and classes may be declared to implement an interface - it doesn't change their value/reference type nature, but a struct stored in a variable typed as an interface will be boxed.结构体和类都可以声明来实现接口——它不会改变它们的值/引用类型性质,但存储在类型为接口的变量中的结构体将被装箱。

Difference in Constructor Behavior构造函数行为的差异

One other difference between Reference and Value Types is what the new keyword means when constructing a new object.引用类型和值类型之间的另一个区别是new关键字在构造新对象时的含义。 Consider this class and this struct:考虑这个类和这个结构:

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;
    }
}

They are basically the same, except that CPoint is a class (a reference type) and SPoint is a struct (a value type).它们基本相同,只是 CPoint 是一个类(引用类型),而 SPoint 是一个结构(值类型)。

When you create an instance of SPoint using the two float constructor (remember, it gets a default constructor auto-magically), like this:当您使用两个 float 构造函数创建 SPoint 的实例时(请记住,它会自动获得一个默认构造函数),如下所示:

var sp = new SPoint (42.0, 3.14);

What happens is that the constructor runs and creates a value.发生的事情是构造函数运行并创建一个值。 That value is then copied into the sp variable (which is of type SPoint and large enough to hold a two-float SPoint).然后将该值复制到sp变量中(它的类型为 SPoint 并且足够大以容纳两个浮点数的 SPoint)。

If I do this:如果我这样做:

var cp = new CPoint (42.0, 3.14);

Something very different happens.发生了非常不同的事情。 First, memory is allocated on the managed heap large enough to hold a CPoint (ie, enough to hold two floats plus the overhead of the object being a reference type).首先,在托管堆上分配的内存足够大以容纳一个 CPoint(即,足够容纳两个浮点数加上作为引用类型的对象的开销)。 Then the two-float constructor runs (and that constructor is the only constructor - there is no default constructor (the additional, programmer-written constructor hides the compiler generated default constructor)).然后运行两个浮点构造函数(并且该构造函数是唯一的构造函数 - 没有默认构造函数(额外的、程序员编写的构造函数隐藏了编译器生成的默认构造函数))。 The constructor initializes that new CPoint in the memory allocated on the managed heap.构造函数在托管堆上分配的内存中初始化该新 CPoint。 Finally, a reference to that newly create object is created and copied to the variable cp.最后,创建对新创建对象的引用并将其复制到变量 cp。

Parameter Passing参数传递

Sorry the preamble took so long.对不起,序言花了这么长时间。

Unless otherwise specified, all parameters to functions/methods are passed by value.除非另有说明,函数/方法的所有参数都是按值传递的。 But, don't forget that the value of a variable of reference type is a reference.但是,不要忘记引用类型变量的值是一个引用。

So, if I have a function declared as (MyType is the class declared above):所以,如果我有一个函数声明为(MyType 是上面声明的类):

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

and some code that looks like:和一些看起来像的代码:

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

What happens is that the value of dec1 is copied to the function parameter (decValue) and available for use within MyFunction.所发生的情况是dec1的值被复制到函数参数 (decValue) 中并可在 MyFunction 中使用。 If someone changes the value of the decValue within the function, no side effects outside the function occurs.如果有人在函数内更改了 decValue 的值,则不会发生函数外的副作用。

Similarly, but differently, the value of mt1 is copied to the method parameter myObject.同样,但不同的是,mt1 的值被复制到方法参数 myObject 中。 However, that value is reference to a MyType object residing on the managed heap.但是,该值是对驻留在托管堆上的 MyType 对象的引用。 If, within the method, some code mutates that object (say: myObject.TheValue=666;), then the object to which both the mt1 and myObject variables refer is mutated, and that results in a side effect viewable outside of the function.如果在该方法中,某些代码改变了该对象(例如:myObject.TheValue=666;),则 mt1 和 myObject 变量所引用的对象发生了变异,这会导致在函数外部可见的副作用。 That said, everything is still being passed by value.也就是说,一切仍然通过价值传递。

Passing Parameters by Reference通过引用传递参数

This is where your question gets answered这是您的问题得到解答的地方

You can pass parameters by reference in two ways, using either the out or ref keywords.您可以通过两种方式通过引用传递参数,使用outref关键字。 An out parameter does not need to be initialized before the function call (while a ref parameter must be).在函数调用之前不需要初始化 out 参数(而必须初始化 ref 参数)。 Within the function, an out parameter must be initialized before the function returns - ref parameters may be initialized, but they do not need to be.在函数内,必须在函数返回之前初始化 out 参数 - 可以初始化 ref 参数,但它们不需要。 The idea is that ref parameters expect to be passed in and out of the function (by reference).这个想法是 ref 参数期望传入和传出函数(通过引用)。 But out parameters are designed simply as a way to pass something out of the function (by reference).但是 out 参数只是被设计为一种将某些东西从函数中传递出去的方式(通过引用)。

If I declare a function like:如果我声明一个函数,如:

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};
}

and then I call it this way然后我这样称呼它

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

After that call, dec1 will contain the value 25.624;在该调用之后,dec1 将包含值 25.624; that value was passed out of the function by reference.该值是通过引用从函数中传递出来的。

Passing reference type variables by reference is more interesting.通过引用传递引用类型变量更有趣。 After the function call, mt1 will no longer refer to the object created with TheValue equal to 5, it will refer to the newly created object with TheValue equal to 5 + 2 (the object created within the function).函数调用后,mt1 将不再引用 TheValue 等于 5 创建的对象,而是引用 TheValue 等于 5 + 2 的新创建对象(在函数内创建的对象)。 Now, mt1 and mt2 will refer to different object with different TheValue property values.现在, mt1 和 mt2 将引用具有不同 TheValue 属性值的不同对象。

With reference types, when you pass a variable normally, the object you pass it may mutate (and that mutation is visible after the function returns).对于引用类型,当您正常传递变量时,传递给它的对象可能会发生变异(并且该变异在函数返回后可见)。 If you pass a reference by reference, the reference itself may mutate, and the value of the reference may be different after the function returns.如果通过引用传递引用,引用本身可能会发生变异,并且函数返回后引用的值可能会有所不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM