diff --git a/docs/csharp/language-reference/keywords/method-parameters.md b/docs/csharp/language-reference/keywords/method-parameters.md index 0136fbbabdde7..187d2c450bb57 100644 --- a/docs/csharp/language-reference/keywords/method-parameters.md +++ b/docs/csharp/language-reference/keywords/method-parameters.md @@ -1,49 +1,43 @@ --- -title: "Method Parameters" -description: "Method parameters are passed by value. Modifiers enable pass-by-reference semantics, including distinctions such as read-only, and `out` parameters. The params modifier allows a series of optional arguments." -ms.date: 05/17/2024 +title: "Method parameters and modifiers" +description: "Parameter modifiers enable pass-by-reference semantics, with distinctions for read-only, and `out` parameters. The `params` modifier allows optional arguments." +ms.date: 11/19/2024 helpviewer_keywords: - "methods [C#], parameters" - "method parameters [C#]" - "parameters [C#]" --- -# Method Parameters +# Method parameters and modifiers -By default, arguments in C# are passed to functions *by value*. That means a copy of the variable is passed to the method. For value (`struct`) types, a copy of the *value* is passed to the method. For reference (`class`) types, a copy of the *reference* is passed to the method. Parameter modifiers enable you to pass arguments *by reference*. The following concepts help you understand these distinctions and how to use the parameter modifiers: +By default, arguments in C# are passed to functions *by value*. That means a copy of the variable is passed to the method. For value (`struct`) types, a copy of the *value* is passed to the method. For reference (`class`) types, a copy of the *reference* is passed to the method. Parameter modifiers enable you to pass arguments *by reference*. -- *Pass by value* means **passing a copy of the variable** to the method. -- *Pass by reference* means **passing access to the variable** to the method. -- A variable of a *reference type* contains a reference to its data. -- A variable of a *value type* contains its data directly. +Because a struct is a [value type](../builtin-types/value-types.md), the method receives and operates on a copy of the argument when you pass a struct by value to a method. The method has no access to the original struct in the calling method and therefore can't change it in any way. The method can change only the copy. -Because a struct is a [value type](../builtin-types/value-types.md), the method receives and operates on a copy of the struct argument when you pass a struct by value to a method. The method has no access to the original struct in the calling method and therefore can't change it in any way. The method can change only the copy. +A class instance is a [reference type](reference-types.md) not a value type. When a reference type is passed by value to a method, the method receives a copy of the reference to the instance. Both variables refer to the same object. The parameter is a copy of the reference. The called method can't reassign the instance in the calling method. However, the called method can use the copy of the reference to access the instance members. If the called method changes an instance member, the calling method also sees those changes since it references the same instance. -A class instance is a [reference type](reference-types.md) not a value type. When a reference type is passed by value to a method, the method receives a copy of the reference to the class instance. Both variables refer to the same object. The parameter is a copy of the reference. The called method can't reassign the instance in the calling method. However, the called method can use the copy of the reference to access the instance members. If the called method changes an instance member, the calling method also sees those changes since it references the same instance. +## Pass by value and pass by reference -The output of the following example illustrates the difference. The method `ClassTaker` changes the value of the `willIChange` field because the method uses the address in the parameter to find the specified field of the class instance. The `willIChange` field of the struct in the calling method doesn't change from calling `StructTaker` because the value of the argument is a copy of the struct itself, not a copy of its address. `StructTaker` changes the copy, and the copy is lost when the call to `StructTaker` is completed. +All the examples in this section use the following two `record` types to illustrate the differences between `class` types and `struct` types: -:::code language="csharp" source="./snippets/PassParameters.cs" id="PassByValueOrReference"::: +:::code language="csharp" source="./snippets/PassParameters.cs" id="DataTypes"::: -## Combinations of parameter type and argument mode +The output of the following example illustrates the difference between passing a struct type by value and passing a class type by value. Both `Mutate` methods change property values of its argument. When the parameter is a `struct` type, those changes are made to a copy of the argument's data. When the parameter is a `class` type, those changes are made to the instance referred to by the argument: -How an argument is passed, and whether it's a reference type or value type controls what modifications made to the argument are visible from the caller: +:::code language="csharp" source="./snippets/PassParameters.cs" id="PassTypesByValue"::: -- When you pass a *value* type *by value*: - - If the method assigns the parameter to refer to a different object, those changes **aren't** visible from the caller. - - If the method modifies the state of the object referred to by the parameter, those changes **aren't** visible from the caller. -- When you pass a *reference* type *by value*: - - If the method assigns the parameter to refer to a different object, those changes **aren't** visible from the caller. - - If the method modifies the state of the object referred to by the parameter, those changes **are** visible from the caller. -- When you pass a *value* type *by reference*: - - If the method assigns the parameter to refer to a different object using `ref =`, those changes **aren't** visible from the caller. - - If the method modifies the state of the object referred to by the parameter, those changes **are** visible from the caller. -- When you pass a *reference* type *by reference*: - - If the method assigns the parameter to refer to a different object, those changes **are** visible from the caller. - - If the method modifies the state of the object referred to by the parameter, those changes **are** visible from the caller. +The `ref` modifier is one way to pass arguments *by reference* to methods. The following code follows the preceding example, but passes parameters by reference. The modifications made to the `struct` type are visible in the calling method when the struct is passed by reference. There's no semantic change when a reference type is passed by reference: -Passing a reference type by reference enables the called method to replace the object to which the reference parameter refers in the caller. The storage location of the object is passed to the method as the value of the reference parameter. If you change the value in the storage location of the parameter (to point to a new object), you also change the storage location to which the caller refers. The following example passes an instance of a reference type as a `ref` parameter. +:::code language="csharp" source="./snippets/PassParameters.cs" id="PassTypesByReference"::: -:::code language="csharp" source="snippets/RefParameterModifier.cs" id="Snippet3"::: +The preceding examples modified properties of a parameter. A method can also reassign a parameter to a new value. Reassignment behaves differently for struct and class types when passed by value or by reference. The following example shows how struct types and class types behave when parameters that are passed by value are reassigned: + +:::code language="csharp" source="./snippets/PassParameters.cs" id="PassByValueReassignment"::: + +The preceding sample shows that when you reassign a parameter to a new value, that change isn't visible from the calling method, regardless of whether the type is a value type or a reference type. The following example shows the behavior when you reassign a parameter that has been passed by reference: + +:::code language="csharp" source="./snippets/PassParameters.cs" id="PassByReferenceReassignment"::: + +The preceding example shows how reassigning the value of a parameter that is passed by reference is visible in the calling context. ## Safe context of references and values @@ -63,6 +57,8 @@ You apply one of the following modifiers to a parameter declaration to pass argu - [`ref readonly`](#ref-readonly-modifier): The argument must be initialized before calling the method. The method can't assign a new value to the parameter. - [`in`](#in-parameter-modifier): The argument must be initialized before calling the method. The method can't assign a new value to the parameter. The compiler might create a temporary variable to hold a copy of the argument to `in` parameters. +A parameter that is passed by reference is a *reference variable*. It doesn't have it's own value. Instead, it refers to a different variable called its *referrent*. Reference variables can be [ref reassigned](../operators/assignment-operator.md#ref-assignment), which changes its referrent. + Members of a class can't have signatures that differ only by `ref`, `ref readonly`, `in`, or `out`. A compiler error occurs if the only difference between two members of a type is that one of them has a `ref` parameter and the other has an `out`, `ref readonly`, or `in` parameter. However, methods can be overloaded when one method has a `ref`, `ref readonly`, `in`, or `out` parameter and the other has a parameter that is passed by value, as shown in the following example. In other situations that require signature matching, such as hiding or overriding, `in`, `ref`, `ref readonly`, and `out` are part of the signature and don't match each other. When a parameter has one of the preceding modifiers, the corresponding argument can have a compatible modifier: @@ -234,6 +230,6 @@ The following example demonstrates various ways in which arguments can be sent t :::code language="csharp" source="snippets/ParameterModifiers.cs" id="ParamsModifierExamples"::: -Overload resolution can cause ambiguity when the argument for a `params` parameter is a collection type. The collection type of the argument must be convertible to the collection type of the parameter. When different overloads provide better conversions for that parameter, that method may be better. However, if the argument to the `params` parameter is either discrete elements or missing, all overloads with different `params` parameter types are equal for that parameter. +Overload resolution can cause ambiguity when the argument for a `params` parameter is a collection type. The collection type of the argument must be convertible to the collection type of the parameter. When different overloads provide better conversions for that parameter, that method might be better. However, if the argument to the `params` parameter is either discrete elements or missing, all overloads with different `params` parameter types are equal for that parameter. -For more details, see the section on [Argument lists](~/_csharpstandard/standard/expressions.md#1262-argument-lists) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. +For more information, see the section on [Argument lists](~/_csharpstandard/standard/expressions.md#1262-argument-lists) in the [C# Language Specification](~/_csharpstandard/standard/README.md). The language specification is the definitive source for C# syntax and usage. diff --git a/docs/csharp/language-reference/keywords/snippets/PassParameters.cs b/docs/csharp/language-reference/keywords/snippets/PassParameters.cs index b33cbe3c10412..8dbb2fd6d619e 100644 --- a/docs/csharp/language-reference/keywords/snippets/PassParameters.cs +++ b/docs/csharp/language-reference/keywords/snippets/PassParameters.cs @@ -1,47 +1,237 @@ using System; -namespace Keywords; +namespace MethodParameters; -// -class TheClass +// +public record struct Point(int X, int Y); +// This doesn't use a primary constructor because the properties implemented for `record` types are +// readonly in record class types. That would prevent the mutations necessary for this example. +public record class Point3D { - public string? willIChange; + public int X { get; set; } + public int Y { get; set; } + public int Z { get; set; } } +// -struct TheStruct +// +public class PassTypesByValue { - public string willIChange; + public static void Mutate(Point pt) + { + Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}"); + pt.X = 19; + pt.Y = 23; + + Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}"); + } + public static void Mutate(Point3D pt) + { + Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}"); + pt.X = 19; + pt.Y = 23; + pt.Z = 42; + + Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}"); + } + + public static void TestPassTypesByValue() + { + Console.WriteLine("===== Value Types ====="); + + var ptStruct = new Point { X = 1, Y = 2 }; + Console.WriteLine($"After initialization:\t\t{ptStruct}"); + + Mutate(ptStruct); + + Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptStruct}"); + + Console.WriteLine("===== Reference Types ====="); + + var ptClass = new Point3D { X = 1, Y = 2, Z = 3 }; + + Console.WriteLine($"After initialization:\t\t{ptClass}"); + + Mutate(ptClass); + Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptClass}"); + + // Output: + // ===== Value Types ===== + // After initialization: Point { X = 1, Y = 2 } + // Enter Mutate: Point { X = 1, Y = 2 } + // Exit Mutate: Point { X = 19, Y = 23 } + // After called Mutate: Point { X = 1, Y = 2 } + // ===== Reference Types ===== + // After initialization: Point3D { X = 1, Y = 2, Z = 3 } + // Enter Mutate: Point3D { X = 1, Y = 2, Z = 3 } + // Exit Mutate: Point3D { X = 19, Y = 23, Z = 42 } + // After called Mutate: Point3D { X = 19, Y = 23, Z = 42 } + } } +// -class TestClassAndStruct + +// +public class PassTypesByReference { - static void ClassTaker(TheClass c) + public static void Mutate(ref Point pt) { - c.willIChange = "Changed"; + Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}"); + pt.X = 19; + pt.Y = 23; + + Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}"); + } + public static void Mutate(ref Point3D pt) + { + Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}"); + pt.X = 19; + pt.Y = 23; + pt.Z = 42; + + Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}"); } - static void StructTaker(TheStruct s) + public static void TestPassTypesByReference() { - s.willIChange = "Changed"; + Console.WriteLine("===== Value Types ====="); + + var pStruct = new Point { X = 1, Y = 2 }; + Console.WriteLine($"After initialization:\t\t{pStruct}"); + + Mutate(ref pStruct); + + Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pStruct}"); + + Console.WriteLine("===== Reference Types ====="); + + var pClass = new Point3D { X = 1, Y = 2, Z = 3 }; + + Console.WriteLine($"After initialization:\t\t{pClass}"); + + Mutate(ref pClass); + Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pClass}"); + + // Output: + // ===== Value Types ===== + // After initialization: Point { X = 1, Y = 2 } + // Enter Mutate: Point { X = 1, Y = 2 } + // Exit Mutate: Point { X = 19, Y = 23 } + // After called Mutate: Point { X = 19, Y = 23 } + // ===== Reference Types ===== + // After initialization: Point3D { X = 1, Y = 2, Z = 3 } + // Enter Mutate: Point3D { X = 1, Y = 2, Z = 3 } + // Exit Mutate: Point3D { X = 19, Y = 23, Z = 42 } + // After called Mutate: Point3D { X = 19, Y = 23, Z = 42 } } +} +// - public static void Main() +// +public class PassByValueReassignment +{ + public static void Reassign(Point pt) { - TheClass testClass = new TheClass(); - TheStruct testStruct = new TheStruct(); + Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}"); + pt = new Point { X = 13, Y = 29 }; - testClass.willIChange = "Not Changed"; - testStruct.willIChange = "Not Changed"; + Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}"); + } - ClassTaker(testClass); - StructTaker(testStruct); + public static void Reassign(Point3D pt) + { + Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}"); + pt = new Point3D { X = 13, Y = 29, Z = -42 }; - Console.WriteLine("Class field = {0}", testClass.willIChange); - Console.WriteLine("Struct field = {0}", testStruct.willIChange); + Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}"); + } + + public static void TestPassByValueReassignment() + { + Console.WriteLine("===== Value Types ====="); + + var ptStruct = new Point { X = 1, Y = 2 }; + Console.WriteLine($"After initialization:\t\t{ptStruct}"); + + Reassign(ptStruct); + + Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}"); + + Console.WriteLine("===== Reference Types ====="); + + var ptClass = new Point3D { X = 1, Y = 2, Z = 3 }; + + Console.WriteLine($"After initialization:\t\t{ptClass}"); + + Reassign(ptClass); + Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}"); + + // Output: + // ===== Value Types ===== + // After initialization: Point { X = 1, Y = 2 } + // Enter Reassign: Point { X = 1, Y = 2 } + // Exit Reassign: Point { X = 13, Y = 29 } + // After called Reassign: Point { X = 1, Y = 2 } + // ===== Reference Types ===== + // After initialization: Point3D { X = 1, Y = 2, Z = 3 } + // Enter Reassign: Point3D { X = 1, Y = 2, Z = 3 } + // Exit Reassign: Point3D { X = 13, Y = 29, Z = -42 } + // After called Reassign: Point3D { X = 1, Y = 2, Z = 3 } } } -/* Output: - Class field = Changed - Struct field = Not Changed -*/ -// +// + +// +public class PassByReferenceReassignment +{ + public static void Reassign(ref Point pt) + { + Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}"); + pt = new Point { X = 13, Y = 29 }; + + Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}"); + } + + public static void Reassign(ref Point3D pt) + { + Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}"); + pt = new Point3D { X = 13, Y = 29, Z = -42 }; + + Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}"); + } + + public static void TestPassByReferenceReassignment() + { + Console.WriteLine("===== Value Types ====="); + + var ptStruct = new Point { X = 1, Y = 2 }; + Console.WriteLine($"After initialization:\t\t{ptStruct}"); + + Reassign(ref ptStruct); + + Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}"); + + Console.WriteLine("===== Reference Types ====="); + + var ptClass = new Point3D { X = 1, Y = 2, Z = 3 }; + + Console.WriteLine($"After initialization:\t\t{ptClass}"); + + Reassign(ref ptClass); + Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}"); + + // Output: + // ===== Value Types ===== + // After initialization: Point { X = 1, Y = 2 } + // Enter Reassign: Point { X = 1, Y = 2 } + // Exit Reassign: Point { X = 13, Y = 29 } + // After called Reassign: Point { X = 13, Y = 29 } + // ===== Reference Types ===== + // After initialization: Point3D { X = 1, Y = 2, Z = 3 } + // Enter Reassign: Point3D { X = 1, Y = 2, Z = 3 } + // Exit Reassign: Point3D { X = 13, Y = 29, Z = -42 } + // After called Reassign: Point3D { X = 13, Y = 29, Z = -42 } + } +} +// + diff --git a/docs/csharp/language-reference/keywords/snippets/Program.cs b/docs/csharp/language-reference/keywords/snippets/Program.cs index 773ccfc0e148e..338dbd7afa91e 100644 --- a/docs/csharp/language-reference/keywords/snippets/Program.cs +++ b/docs/csharp/language-reference/keywords/snippets/Program.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using MethodParameters; namespace Keywords { @@ -12,7 +13,14 @@ static async Task Main(string[] args) Console.WriteLine("================= readonly Keyword Examples ======================"); ReadonlyKeywordExamples.Examples(); Console.WriteLine("================= pass by value / reference Keyword Examples ======================"); - TestClassAndStruct.Main(); + PassTypesByValue.TestPassTypesByValue(); + Console.WriteLine("===="); + PassTypesByReference.TestPassTypesByReference(); + Console.WriteLine("===="); + PassByValueReassignment.TestPassByValueReassignment(); + Console.WriteLine("===="); + PassByReferenceReassignment.TestPassByReferenceReassignment(); + Console.WriteLine("===="); ParameterModifiers.ParamPassingExamples(); } } diff --git a/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs b/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs index 771823d58526d..49b66327a7f7f 100644 --- a/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs +++ b/docs/csharp/language-reference/keywords/snippets/RefParameterModifier.cs @@ -9,8 +9,6 @@ public class RefParameterModifier public static void Examples() { FirstRefExample(); - ModifyProductsByReference(); - // call By Ref examples: var options = new OptionStruct(); @@ -38,44 +36,6 @@ void Method(ref int refArgument) // } - // - class Product - { - public Product(string name, int newID) - { - ItemName = name; - ItemID = newID; - } - - public string ItemName { get; set; } - public int ItemID { get; set; } - } - - private static void ChangeByReference(ref Product itemRef) - { - // Change the address that is stored in the itemRef parameter. - itemRef = new Product("Stapler", 12345); - } - - private static void ModifyProductsByReference() - { - // Declare an instance of Product and display its initial values. - Product item = new Product("Fasteners", 54321); - System.Console.WriteLine("Original values in Main. Name: {0}, ID: {1}\n", - item.ItemName, item.ItemID); - - // Pass the product instance to ChangeByReference. - ChangeByReference(ref item); - System.Console.WriteLine("Calling method. Name: {0}, ID: {1}\n", - item.ItemName, item.ItemID); - } - - // This method displays the following output: - // Original values in Main. Name: Fasteners, ID: 54321 - // Calling method. Name: Stapler, ID: 12345 - - // - private static void BookCollectionExample() { // diff --git a/docs/csharp/language-reference/operators/assignment-operator.md b/docs/csharp/language-reference/operators/assignment-operator.md index 564148d351003..00a7bef2ef884 100644 --- a/docs/csharp/language-reference/operators/assignment-operator.md +++ b/docs/csharp/language-reference/operators/assignment-operator.md @@ -1,7 +1,7 @@ --- title: "Assignment operators - assign an expression to a variable" -description: "C# assignment operators assign an expression to a variable. Assignment sets the value of the expression. `ref` assignment sets the reference of a `ref` variable." -ms.date: 11/29/2022 +description: "C# Assignment sets the value of the expression. Alternatively, `ref` assignment sets the reference of a reference variable." +ms.date: 11/21/2024 f1_keywords: - "=_CSharpKeyword" helpviewer_keywords: @@ -18,7 +18,7 @@ The assignment operator `=` is right-associative, that is, an expression of the a = b = c ``` -is evaluated as +Is evaluated as ```csharp a = (b = c) @@ -30,7 +30,7 @@ The following example demonstrates the usage of the assignment operator with a l The left-hand operand of an assignment receives the *value* of the right-hand operand. When the operands are of [value types](../builtin-types/value-types.md), assignment copies the contents of the right-hand operand. When the operands are of [reference types](../builtin-types/reference-types.md), assignment copies the reference to the object. -This is called *value assignment*: the value is assigned. +This operation is called *value assignment*: the value is assigned. ## ref assignment @@ -42,6 +42,14 @@ In the preceding example, the [local reference variable](../statements/declarati The left-hand operand of `ref` assignment can be a [local reference variable](../statements/declarations.md#reference-variables), a [`ref` field](../builtin-types/ref-struct.md#ref-fields), and a [`ref`](../keywords/ref.md), [`out`](../keywords/method-parameters.md#out-parameter-modifier), or [`in`](../keywords/method-parameters.md#in-parameter-modifier) method parameter. Both operands must be of the same type. +A `ref` assignment means that a reference variable has a different referrent. It's no longer referring to its previous referrent. Using `ref =` on a `ref` parameter means the parameter no longer refers to its argument. Any actions that modify the state of the object after ref reassigning it make those modifications to the new item. For example, consider the following method: + +:::code language="csharp" source="snippets/shared/AssignmentOperator.cs" id="SnippetRefReassignAndModify"::: + +The following usage shows that the assignment to the parameter `s` isn't visible after the method call because `s` was `ref` reassigned to refer to `sLocal` before the string was modified: + +:::code language="csharp" source="snippets/shared/AssignmentOperator.cs" id="Usage"::: + ## Compound assignment For a binary operator `op`, a compound assignment expression of the form @@ -50,15 +58,15 @@ For a binary operator `op`, a compound assignment expression of the form x op= y ``` -is equivalent to +Is equivalent to ```csharp x = x op y ``` -except that `x` is only evaluated once. +Except that `x` is only evaluated once. -Compound assignment is supported by [arithmetic](arithmetic-operators.md#compound-assignment), [Boolean logical](boolean-logical-operators.md#compound-assignment), and [bitwise logical and shift](bitwise-and-shift-operators.md#compound-assignment) operators. +The [arithmetic](arithmetic-operators.md#compound-assignment), [Boolean logical](boolean-logical-operators.md#compound-assignment), and [bitwise logical and shift](bitwise-and-shift-operators.md#compound-assignment) operators all support compount assignment. ## Null-coalescing assignment diff --git a/docs/csharp/language-reference/operators/snippets/shared/AssignmentOperator.cs b/docs/csharp/language-reference/operators/snippets/shared/AssignmentOperator.cs index 147a902fb11f0..29d5b4c4c6904 100644 --- a/docs/csharp/language-reference/operators/snippets/shared/AssignmentOperator.cs +++ b/docs/csharp/language-reference/operators/snippets/shared/AssignmentOperator.cs @@ -6,6 +6,11 @@ public static void Examples() { Simple(); RefAssignment(); + // + string msg = "Hi"; + RefReassignAndModify(ref msg); + Console.WriteLine(msg); // Output: Hi! + // } private static void Simple() @@ -53,4 +58,16 @@ private static void RefAssignment() // 3 0 5 // } + + // + private static void RefReassignAndModify(scoped ref string s) + { + string sLocal = "Hello"; + Console.WriteLine(sLocal); // Output: Hello + + s = ref sLocal; + s = "World"; + Console.WriteLine(s); // Output: World + // + } }