Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Class

`params` parameters

<Return> <Name>(params <T>[] <rest>) { … } <Return> <Name>(<T> <first>, params <T>[] <rest>) { … }

`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.

stable4 examples compiled by CIclassmethodsparameters

Summary#

params collects the trailing arguments into an array:

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#

<Return> <Name>(params <T>[] <rest>) { … }
<Return> <Name>(<T> <first>, params <T>[] <rest>) { … }

Description#

Where can params go, and what type must it be?#

Two rules, both C#'s, and both following from what params does — it collects the rest:

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:

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#

If you already have the array, pass it — it binds to the parameter as-is rather than being wrapped in another array:

[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#

A params method takes part in overload resolution 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.

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?#

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#

Related

class methods

Behaviour attached to a class — a method with a receiver, called as value.Method(). Classes are in-memory values, so a…

Method overloads

A class can declare several methods with the same name, as long as they differ in their parameter types. Each call…

Classes

A class is an in-memory shape — data plus the behaviour that belongs to it — and it never touches the database. That is…

`static` methods

A `static` method belongs to the type rather than to any instance, and is called on the type name — `Money.Round(x)`…