Parameters, overloads, ref/out and params

Four things a parameter list does beyond naming types: carry a default, tell one overload from another, pass by reference, and take a list whose length the call site decides. A function takes at most 12 parameters, a method's receiver being one of them.

Defaults #

A default belongs to the declaration and the call site fills it in — on a free function, a method, a constructor or an interface signature. It is a constant expression, folded where it is declared, and no parameter without a default may follow one that has one. A virtual/override pair keeps one slot and each end carries its own default, so a call through a base-typed name takes the base's default and still runs the override.

Overloading #

One name, several signatures, resolved by arity first and then by argument types; the return type never takes part, and an integer literal is an i64. A call nested in another is resolved first, so its return type types the outer site. Two candidates fitting one site is refused as ambiguous, and so are two signatures differing only by ref/out.

ref and out #

C#'s by-reference parameters, and the call site says so too: bump(ref a), split(57, out hi, out lo). The argument is a name — a local, a parameter, a field or an array element — never an expression and never a call's result.

refout
the callee reads the caller's variableyesnot required
the callee must assign itnoyes; never assigning it is refused
a counted pointee starts releasednoyes, so the first assignment frees nothing

The pointee may be narrower than a word (u8, i32) or a float, and it may be passed on to another ref/out parameter as many levels down as you like. A parameter carrying ref or out takes no default.

params #

params i64[] xs is C#'s own: a modifier before the type, a genuine T[] after it, on the last parameter of a free function. Inside the body xs is an ordinary array — xs.Length, xs[i], foreach, passing it on. At the call site the compiler builds the array out of the arguments, none of them being a limit; a single argument that is already a T[] passes straight through. The element may be a float, or a class, and the array is released at the end of the statement that built it. A fixed parameter may come first, and a variadic call may sit inside another one.

One program #

// expect-exit: 42
#include "rt.tk"

i64 add(i64 a, i64 b = 10) {
    return a + b;
}

i64 pick(i64 n) {
    return n + 1;
}

i64 pick(i64 a, i64 b) {                     // overloaded by arity
    return a * b;
}

void bump(i64 x) {
    x = x + 1;                               // by value: the caller sees nothing
}

void bump(ref i64 x) {
    x = x + 1;                               // overloaded on `ref`
}

void split(i64 v, out i64 hi, out i64 lo) {
    hi = v / 10;
    lo = v % 10;
}

i64 total(params i64[] xs) {
    i64 s = 0;
    i64 i = 0;
    loop {
        if (i >= xs.Length) break;
        s = s + xs[i];
        i = i + 1;
    }
    return s;
}

f64 ftotal(params f64[] xs) {                // a float element travels as a float
    f64 s = 0.0;
    i64 i = 0;
    loop {
        if (i >= xs.Length) break;
        s = s + xs[i];
        i = i + 1;
    }
    return s;
}

i64 main() {
    if (add(1) != 11) return 1;              // the declaration's own default
    if (add(1, 2) != 3) return 2;
    if (pick(9) != 10) return 3;
    if (pick(3, 4) != 12) return 4;

    i64 a = 1;
    bump(a);
    if (a != 1) return 5;
    bump(ref a);                             // the call site says `ref` as well
    if (a != 2) return 6;

    i64 hi = 0;
    i64 lo = 0;
    split(57, out hi, out lo);
    if (hi != 5) return 7;
    if (lo != 7) return 8;

    if (total() != 0) return 9;              // an array of length 0
    if (total(1, 2, 3) != 6) return 10;
    if ((i64) ftotal(1.5, 2.5) != 4) return 11;

    i64[] ready = new i64[2];
    ready[0] = 4;
    ready[1] = 3;
    if (total(ready) != 7) return 12;        // the normal form: no copy

    return add(1) + pick(3, 4) + total(10, 7) + a;
}

A list is one signature among the name's: f(i64) beside f(params i64[]) is C#'s own pair, and f(1) is the first — a candidate that takes the arguments as written wins, and only then is a list asked to swallow the tail. params on a method or a constructor, and f(out i64 a) declaring the variable at the call site, are not taught; the full account is parameters.md.

Edit this page