# `params` parameters

> `params` lets a method be called with any number of trailing arguments — `Total(1m, 2m, 3m)` — which arrive as one array. It must be the last parameter and its type must be an array. Calling with no trailing arguments passes an empty array, so a `params` parameter is optional without a default; passing an actual array of the right type still binds directly, without being wrapped again.

<!-- id: class-params · area: class · stability: stable · html: https://osysharp.com/reference/class/params/ -->

## Summary        {#summary}
`params` collects the trailing arguments into an array:

```osy title="any number of arguments" run app=class-params
class Calc {
  public decimal Total(params decimal[] xs) {
    decimal t = 0m;
    foreach (var x in xs) { t = t + x; }
    return t;
  }
}

[Test]
void Params_Collects_The_Trailing_Arguments() {
  var c = new Calc();
  Assert.Equal(6m, c.Total(1m, 2m, 3m));
  Assert.Equal(0m, c.Total());              // no trailing arguments — an EMPTY array, not null
}
```

## Signature      {#signature}
```osy syntax
<Return> <Name>(params <T>[] <rest>) { … }
<Return> <Name>(<T> <first>, params <T>[] <rest>) { … }
```

## Description    {#description}

### Where can `params` go, and what type must it be?   {#rules}
Two rules, both C#'s, and both following from what `params` does — it collects *the rest*:

```osy title="✗ the three ways params is written wrong" syntax
decimal Sum(params decimal[] xs, string tail) { … }   // ✗ it must be the LAST parameter
decimal Sum(params decimal x) { … }                   // ✗ its type must be an ARRAY
decimal Sum(params decimal[] xs = null) { … }         // ✗ it is already optional; no default
```

A parameter before it is ordinary and keeps its own argument:

```osy title="a fixed parameter, then the rest" run app=class-params
class Report {
  public decimal Offset(decimal start, params decimal[] xs) {
    decimal t = start;
    foreach (var x in xs) { t = t + x; }
    return t;
  }
}

[Test]
void The_Leading_Parameter_Keeps_Its_Own_Argument() {
  var r = new Report();
  Assert.Equal(103m, r.Offset(100m, 1m, 2m));
}
```

### Passing an array directly still works   {#an-actual-array}
If you already have the array, pass it — it binds to the parameter as-is rather than being wrapped in another array:

```osy title="an array binds directly" run app=class-params
[Test]
void An_Actual_Array_Is_Not_Wrapped_Again() {
  var c = new Calc();
  decimal[] values = [4m, 5m];
  Assert.Equal(9m, c.Total(values));       // one array of two — not one array containing one array
}
```

Which of the two readings applies is decided by the argument's **type**, not by how many arguments there are — both
spellings pass exactly one. A `decimal[]` is the array; a `decimal` is one element of it.

### `params` and overloads   {#overloads}
A `params` method takes part in [overload resolution](https://osysharp.com/reference/class/overloads/) like any other, with one rule: the ordinary
reading is tried first for every candidate, and the collecting form is considered only if nothing matched ordinarily.

That has a consequence worth relying on: **adding `params` to an existing method cannot change which overload an
existing call already picks.** It can only make a call compile that did not before.

```osy title="an exact match wins over collecting" run app=class-params
class Fmt {
  public string Of(decimal d) { return "one"; }
  public string Of(params decimal[] ds) { return "many"; }
}

[Test]
void The_Ordinary_Reading_Is_Tried_First() {
  var f = new Fmt();
  Assert.Equal("one", f.Of(1m));            // the exact single-argument method
  Assert.Equal("many", f.Of(1m, 2m));       // only the collecting one can take two
}
```

`params` is **not** part of a method's identity, so `Sum(params decimal[])` and `Sum(decimal[])` are the same method
and cannot both be declared — a call site could not tell them apart.

### What is the collected array, inside the body?   {#no-new-shape}
Inside the body, `rest` is an ordinary array: `foreach` over it, ask for its `Count()`, index it, pass it on. There is
nothing to learn beyond the call form.

## See also       {#see-also}
- [class methods](https://osysharp.com/reference/class/methods/) — methods, and the member-body surface
- [Method overloads](https://osysharp.com/reference/class/overloads/) — how a call site picks from a set of same-named methods
- [`static` methods](https://osysharp.com/reference/class/static/) — `static`, which combines with `params` freely
- [Classes](https://osysharp.com/reference/class/index/) — fields, `const`, and what a class is
