By default, C# passes every argument by value: the method gets a copy, and assigning to the parameter does not affect the caller. The ref, out and in modifiers pass the caller's variable itself instead.
Output:
by value: 50
by ref: 60
The keyword appears twice: in the parameter list (ref int score) and at the call site (ref score). Writing it at the call is mandatory, and it is deliberate: anyone reading AddPoints(ref score) knows score may change. Forget it and the compiler reports CS1620, Argument 1 must be passed with the 'ref' keyword.
ref: Read and Write the Caller's Variable
A ref parameter is an alias for the caller's variable. The method can read the current value and replace it. The classic example is a swap, which is impossible with plain parameters:
Output:
Linus Ada
volume 100
Rules for ref:
- The argument must be a variable (a local, a parameter, a field or an array element), not a literal or an expression. A property does not qualify either:
Clamp(ref player.Volume, 0, 100)is error CS0206,A non ref-returning property or indexer may not be used as an out or ref value. Copy the property into a local, pass that, and assign it back. - The variable must be definitely assigned before the call.
int n; Clamp(ref n, 0, 10);is CS0165,Use of unassigned local variable 'n'.
out: Extra Results
out is for values the method produces. The caller does not need to initialize the variable, and the method must assign it on every path before returning:
static void Split(string fullName, out string first, out string last)
{
// error CS0177: The out parameter 'last' must be assigned to before control leaves the current method
first = fullName.Split(' ')[0];
}
Inside the method, an out parameter counts as unassigned until you write to it, so you cannot read the caller's old value. That is the difference from ref: ref is two-way, out is one-way from the method to the caller.
The TryParse Pattern
The best-known use of out is the Try pattern in the base library: return bool for success and put the result in an out parameter, so bad input never throws:
Output:
"vip25" is a coupon: 25% off
"42" is a quantity: 42
"BOGUS" is not recognized
"" is not recognized
out var quantity (C# 7.0) declares the variable right in the call; its type comes from the parameter. On failure, TryParse sets it to 0 and returns false. When you only care whether parsing succeeds, discard the value with out _: bool isNumber = int.TryParse(input, out _);.
Note how TryGetDiscount assigns rate on both paths: once explicitly and once by forwarding it to Dictionary.TryGetValue, which is itself an out call.
out Versus Returning a Tuple
Before C# 7, out was the main way to return several values. Tuples now do that more directly:
// with out parameters
static void MinMax(int[] values, out int min, out int max) { ... }
MinMax(temps, out int lo, out int hi);
// with a tuple
static (int Min, int Max) MinMax(int[] values) { ... }
var (lo, hi) = MinMax(temps);
Use a tuple (or a small class) for plain multiple results. Keep out for the Try pattern, where the bool return lets the call sit in an if condition.
Reference Types: Changing the Object vs Replacing It
Passing a class instance by value already lets the method change the object, because the copied value is a reference to the same object. ref adds one more ability: replacing the object the caller's variable points to.
Output:
after AddItem: 2 items
after ResetByValue: 2 items
after ResetByRef: 0 items
ResetByValue pointed its own copy of the reference at a new list; the caller never saw it. ResetByRef changed the caller's variable itself. In everyday code you rarely need ref on a class parameter: return the new object instead.
For structs, the difference is bigger, since a struct argument is copied field by field. A method that must modify a caller's struct (a Point, a large settings struct) needs ref.
in: Read-Only by Reference
C# 7.2 added in, which passes by reference but forbids assignment. Its purpose is performance: a large struct is not copied on each call, and the caller is guaranteed it will not change.
// C# 7.2 and later
struct Matrix4
{
public double M11, M12, M13, M14, M21, M22, M23, M24,
M31, M32, M33, M34, M41, M42, M43, M44;
}
static double Trace(in Matrix4 m) => m.M11 + m.M22 + m.M33 + m.M44;
static void Reset(in Matrix4 m)
{
m.M11 = 0;
// error CS8332: Cannot assign to a member of variable 'm' or use it as the right hand side of a ref assignment because it is a readonly variable
}
At the call site in is optional: Trace(matrix) and Trace(in matrix) both work. For int, double, DateTime and other small types, in gains nothing and can be slightly slower; use it for structs of several fields that you pass often.
Restrictions
refandoutparameters cannot be optional (CS1741). Aninparameter can:static void Connect(in int retries = 3)is legal.- Overloads cannot differ only between
refandout(orin), because they are the same at the runtime level.F(int x)andF(ref int x)can coexist. asyncmethods and iterators (methods usingyield return) cannot haveref,outorinparameters. Return a tuple or a result object from those instead.- A lambda or local function cannot capture a
ref,outorinparameter of the enclosing method; copy it into a local first.
ref, out and in Compared
| Caller must assign first | Method must assign | Method may read | Call-site keyword | |
|---|---|---|---|---|
| (none) | yes | no | yes (a copy) | none |
ref | yes | no | yes | required |
out | no | yes, on every path | only after assigning | required |
in | yes | not allowed | yes | optional |
Frequently Asked Questions
What is the difference between ref and out in C#?
Both pass the caller's variable itself rather than a copy. With ref, the variable must be assigned before the call and the method may read and change it. With out, the variable need not be assigned first, the method cannot read it before writing, and it must assign it before returning (CS0177 otherwise). out means "this is an extra result".
What does out var mean in C#?
Since C# 7.0 you can declare the out variable inside the call: if (int.TryParse(text, out var number)). The variable's type is inferred from the parameter, and it is in scope after the statement too. out _ discards a result you do not need.
Are objects passed by reference in C#?
No, every argument is passed by value unless you write ref, out or in. For a class, the value that gets copied is a reference, so the method can change the object's fields and the caller sees it. What it cannot do without ref is make the caller's variable point to a different object.
What is the in parameter modifier in C#?
in (C# 7.2 and later) passes an argument by reference but read-only: the method cannot assign to it. It exists to avoid copying large structs on every call. For small types such as int or DateTime it brings no benefit.
Why do I get "Argument 1 must be passed with the 'ref' keyword"?
The method declares a ref parameter and the call left the keyword out. C# requires ref (and out) at the call site as well, so readers can see that the variable may change: Increment(ref count);, not Increment(count);.