- ref (C# reference)
- Passing an argument by reference: An example
- Reference return values
- A ref returns and ref locals example
- C# language specification
- See also
- Си шарп ref out
- Передача параметров по значению
- Передача параметров по ссылке и модификатор ref
- Выходные параметры. Модификатор out
- Входные параметры. Модификатор in
ref (C# reference)
When used in a method’s parameter list, the ref keyword indicates that an argument is passed by reference, not by value. The ref keyword makes the formal parameter an alias for the argument, which must be a variable. In other words, any operation on the parameter is made on the argument.
For example, suppose the caller passes a local variable expression or an array element access expression. The called method can then replace the object to which the ref parameter refers. In that case, the caller’s local variable or the array element refers to the new object when the method returns.
Don’t confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same. A method parameter can be modified by ref regardless of whether it is a value type or a reference type. There is no boxing of a value type when it is passed by reference.
To use a ref parameter, both the method definition and the calling method must explicitly use the ref keyword, as shown in the following example. (Except that the calling method can omit ref when making a COM call.)
void Method(ref int refArgument) < refArgument = refArgument + 44; >int number = 1; Method(ref number); Console.WriteLine(number); // Output: 45
An argument that is passed to a ref or in parameter must be initialized before it’s passed. This requirement differs from out parameters, whose arguments don’t have to be explicitly initialized before they’re passed.
Members of a class can’t have signatures that differ only by ref , 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 , or in parameter. The following code, for example, doesn’t compile.
class CS0663_Example < // Compiler error CS0663: "Cannot define overloaded // methods that differ only on ref and out". public void SampleMethod(out int i) < >public void SampleMethod(ref int i) < >>
However, methods can be overloaded when one method has a ref , in , or out parameter and the other has a parameter that is passed by value, as shown in the following example.
class RefOverloadExample < public void SampleMethod(int i) < >public void SampleMethod(ref int i) < >>
In other situations that require signature matching, such as hiding or overriding, in , ref , and out are part of the signature and don’t match each other.
Properties aren’t variables. They’re methods, and can’t be passed to ref parameters.
You can’t use the ref , in , and out keywords for the following kinds of methods:
- Async methods, which you define by using the async modifier.
- Iterator methods, which include a yield return or yield break statement.
Extension methods also have restrictions on the use of these keywords:
- The out keyword can’t be used on the first argument of an extension method.
- The ref keyword can’t be used on the first argument of an extension method when the argument isn’t a struct, or a generic type not constrained to be a struct.
- The in keyword can’t be used unless the first argument is a struct. The in keyword can’t be used on any generic type, even when constrained to be a struct.
Passing an argument by reference: An example
The previous examples pass value types by reference. You can also use the ref keyword to pass reference types 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.
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", 99999); // You can change the value of one of the properties of // itemRef. The change happens to item in Main as well. itemRef.ItemID = 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: , ID: \n", item.ItemName, item.ItemID); // Pass the product instance to ChangeByReference. ChangeByReference(ref item); System.Console.WriteLine("Back in Main. Name: , ID: \n", item.ItemName, item.ItemID); > // This method displays the following output: // Original values in Main. Name: Fasteners, ID: 54321 // Back in Main. Name: Stapler, ID: 12345
For more information about how to pass reference types by value and by reference, see Passing Reference-Type Parameters.
Reference return values
Reference return values (or ref returns) are values that a method returns by reference to the caller. That is, the caller can modify the value returned by a method, and that change is reflected in the state of the object in the called method.
A reference return value is defined by using the ref keyword:
- In the method signature. For example, the following method signature indicates that the GetCurrentPrice method returns a Decimal value by reference.
public ref decimal GetCurrentPrice()
- Between the return token and the variable returned in a return statement in the method. For example:
In order for the caller to modify the object’s state, the reference return value must be stored to a variable that is explicitly defined as a reference variable.
Here’s a more complete ref return example, showing both the method signature and method body.
public static ref int Find(int[,] matrix, Func predicate)
The called method may also declare the return value as ref readonly to return the value by reference, and enforce that the calling code can’t modify the returned value. The calling method can avoid copying the returned value by storing the value in a local ref readonly reference variable.
A ref returns and ref locals example
The following example defines a Book class that has two String fields, Title and Author . It also defines a BookCollection class that includes a private array of Book objects. Individual book objects are returned by reference by calling its GetBookByTitle method.
public class Book < public string Author; public string Title; >public class BookCollection < private Book[] books = < new Book < Title = "Call of the Wild, The", Author = "Jack London" >, new Book < Title = "Tale of Two Cities, A", Author = "Charles Dickens" >>; private Book nobook = null; public ref Book GetBookByTitle(string title) < for (int ctr = 0; ctr < books.Length; ctr++) < if (title == books[ctr].Title) return ref books[ctr]; >return ref nobook; > public void ListBooks() < foreach (var book in books) < Console.WriteLine($", by "); > Console.WriteLine(); > >
When the caller stores the value returned by the GetBookByTitle method as a ref local, changes that the caller makes to the return value are reflected in the BookCollection object, as the following example shows.
var bc = new BookCollection(); bc.ListBooks(); ref var book = ref bc.GetBookByTitle("Call of the Wild, The"); if (book != null) book = new Book < Title = "Republic, The", Author = "Plato" >; bc.ListBooks(); // The example displays the following output: // Call of the Wild, The, by Jack London // Tale of Two Cities, A, by Charles Dickens // // Republic, The, by Plato // Tale of Two Cities, A, by Charles Dickens
C# language specification
For more information, see the C# Language Specification. The language specification is the definitive source for C# syntax and usage.
See also
Си шарп ref out
Существует два способа передачи параметров в метод в языке C#: по значению и по ссылке .
Передача параметров по значению
Наиболее простой способ передачи параметров представляет передача по значению, по сути это обычный способ передачи параметров:
void Increment(int n) < n++; Console.WriteLine($"Число в методе Increment: "); > int number = 5; Console.WriteLine($"Число до метода Increment: "); Increment(number); Console.WriteLine($"Число после метода Increment: ");
Число до метода Increment: 5 Число в методе Increment: 6 Число после метода Increment: 5
При передаче аргументов параметрам по значению параметр метода получает не саму переменную, а ее копию и далее работает с этой копией независимо от самой переменной.
Так, выше при вызове метод Increment получает копию переменной number и увеличивает значение этой копии. Поэтому в самом методе Increment мы видим, что значение параметра n увеличилось на 1, но после выполнения метода переменная number имеет прежнее значение — 5. То есть изменяется копия, а сама переменная не изменяется.
Передача параметров по ссылке и модификатор ref
При передаче параметров по ссылке перед параметрами используется модификатор ref :
void Increment(ref int n) < n++; Console.WriteLine($"Число в методе Increment: "); > int number = 5; Console.WriteLine($"Число до метода Increment: "); Increment(ref number); Console.WriteLine($"Число после метода Increment: ");
Число до метода Increment: 5 Число в методе Increment: 6 Число после метода Increment: 6
При передаче значений параметрам по ссылке метод получает адрес переменной в памяти. И, таким образом, если в методе изменяется значение параметра, передаваемого по ссылке, то также изменяется и значение переменной, которая передается на его место..
Так, в метод Increment передается ссылка на саму переменную number в памяти. И если значение параметра n в Increment изменяется, то это приводит и к изменению переменной number, так как и параметр n и переменная number указывают на один и тот же адрес в памяти.
Обратите внимание, что модификатор ref указывается как перед параметром при объявлении метода, так и при вызове метода перед аргументом, который передается параметру.
Выходные параметры. Модификатор out
Выше мы использовали входные параметры. Но параметры могут быть также выходными. Чтобы сделать параметр выходным, перед ним ставится модификатор out :
void Sum(int x, int y, out int result)
Здесь результат возвращается не через оператор return , а через выходной параметр result. Использование в программе:
void Sum(int x, int y, out int result) < result = x + y; >int number; Sum(10, 15, out number); Console.WriteLine(number); // 25
Причем, как и в случае с ref ключевое слово out используется как при определении метода, так и при его вызове.
Также обратите внимание, что методы, использующие такие параметры, обязательно должны присваивать им определенное значение. То есть следующий код будет недопустим, так как в нем для out-параметра не указано никакого значения:
void Sum(int x, int y, out int result)
Прелесть использования подобных параметров состоит в том, что по сути мы можем вернуть из метода не одно значение, а несколько. Например:
void GetRectangleData(int width, int height, out int rectArea, out int rectPerimetr) < rectArea = width * height; // площадь прямоугольника - произведение ширины на высоту rectPerimetr = (width + height) * 2; // периметр прямоугольника - сумма длин всех сторон >int area; int perimetr; GetRectangleData(10, 20, out area, out perimetr); Console.WriteLine($"Площадь прямоугольника: "); // 200 Console.WriteLine($"Периметр прямоугольника: "); // 60
Здесь у нас есть метод GetRectangleData, который получает ширину и высоту прямоугольника (параметры width и height). А два выходных параметра мы используем для подсчета площади и периметра прямоугольника.
При этом можно определять переменные, которые передаются out -параметрам в непосредственно при вызове метода. То есть мы можем сократить предыдущий пример следующим образом:
void GetRectangleData(int width, int height, out int rectArea, out int rectPerimetr) < rectArea = width * height; rectPerimetr = (width + height) * 2; >GetRectangleData(10, 20, out int area, out int perimetr); Console.WriteLine($"Площадь прямоугольника: "); // 200 Console.WriteLine($"Периметр прямоугольника: "); // 60
При этом, если нам неизвестен тип значений, которые будут присвоены параметрам, то мы можем для их определения использовать оператор var :
GetRectangleData(10, 20, out var area, out var perimetr); Console.WriteLine($"Площадь прямоугольника: "); // 200 Console.WriteLine($"Периметр прямоугольника: "); // 60
Входные параметры. Модификатор in
Кроме выходных параметров с модификатором out метод может использовать входные параметры с модификатором in . Модификатор in указывает, что данный параметр будет передаваться в метод по ссылке, однако внутри метода его значение параметра нельзя будет изменить. Например, возьмем следующий метод:
void GetRectangleData(in int width, in int height, out int rectArea, out int rectPerimetr) < //width = 25; // нельзя изменить, так как width - входной параметр rectArea = width * height; rectPerimetr = (width + height) * 2; >int w = 10; int h = 20; GetRectangleData(w, h, out var area, out var perimetr); Console.WriteLine($"Площадь прямоугольника: "); // 200 Console.WriteLine($"Периметр прямоугольника: "); // 60
В данном случае через входные параметры width и height в метод передаются значения, но в самом методе мы не можем изменить значения этих параметров, так как они определены с модификатором in .
Передача по ссылке в некоторых случаях может увеличить произодительность, а использование оператора in гарантирует, что значения переменных, которые передаются параметрам, нельзя будет изменить в этом методе.