The concept and environment
C# 12 permits using aliases for additional type shapes, including tuples and arrays. Window names a tuple with Start and Count elements, while Scores names an integer array. These are source-level alternative names, not declarations of new domain types. An alias can make a local implementation easier to read, but it does not introduce construction validation, new runtime identity, or ownership independence. In particular, an array alias retains all ordinary array reference semantics. Read the expanded underlying type when deciding assignment compatibility or how mutations will propagate through a variable.
Walk through the portable program
The tuple literal supplies start two and count three, which are printed through the named elements. original then refers to a two-element integer array. Assigning it to alias does not construct another array; both variables refer to the same storage. Changing alias at index zero writes nine into that shared array, so reading original at that index also gives nine. ReferenceEquals consequently prints true. The three lines are window:2:3, original:9, and same:True. The names Scores and alias provide readability but do not change the assignment's underlying semantics.
A deeper design boundary
Tuple element names help readers access and understand positions, but they are not a nominal boundary that distinguishes two domain concepts with identical element types. An ordinary compatible tuple can be assigned to Window. If a public API must prevent accidentally substituting a screen coordinate for a pagination window, define an appropriate named type and validate its contract. Likewise, aliases do not automatically become exported names that consuming assemblies can use. Keep a local convenience alias separate from a deliberate public record, class, or struct intended to carry an application's domain meaning.
Pitfalls and limits
Avoid treating an alias as either a copy operation or a nonnegative-value guarantee. Window can contain a negative Count unless your own validation rejects it. Scores can contain arbitrary integers and remains a mutable array reference. Alias scope also matters: a regular using directive applies within its declaration scope, whereas a global using changes availability across a compilation. Neither form changes runtime object layout. Keep the example aliases above type and top-level declarations, and do not confuse a type alias with importing a namespace that contains many independently declared types.
Practice one: isolate a contract
First assign a separately declared (int, int) value to a Window variable and predict that it remains compatible. Then give its elements different local names and inspect how the receiving Window variable exposes Start and Count. Hint: reason from the positional element types, not from an assumption that names create a new nominal type. Add a validation function for nonnegative count and write a negative-count case that demonstrates the alias itself performs no checking. Keep successful assignment and domain acceptance as two separate observations.
Practice two: challenge an assumption
For another exercise, create a fresh array copy before assigning it to a Scores variable, mutate that copy, and predict that the original array retains four at index zero. Hint: use an explicit copy such as ToArray with its namespace available; writing a second alias declaration is insufficient. Verify both element values and ReferenceEquals. Then replace the alias with a small named wrapper and decide whether its constructor copies incoming arrays or retains them. Test that documented ownership choice rather than assuming a wrapper automatically fixes reference sharing.
Review the resulting contract
Use aliases when they clarify implementation vocabulary without pretending to supply guarantees they cannot express. Before exposing a related API, ask whether callers need a reusable nominal type, validation, equality rules, or an ownership boundary. The sample intentionally pairs tuple compatibility with array aliasing so those concerns cannot be conflated. A readable name is useful, but it does not make incompatible concepts distinguishable to the type system unless a real type is declared. Keep that distinction visible in code review and in tests that exercise actual values and mutations.
A working example
using System;
using Window = (int Start, int Count);
using Scores = int[];
Window window = (2, 3);
Scores original = [4, 5];
Scores alias = original;
alias[0] = 9;
Console.WriteLine($"window:{window.Start}:{window.Count}");
Console.WriteLine($"original:{original[0]}");
Console.WriteLine($"same:{ReferenceEquals(original, alias)}");
Expected output
window:2:3 original:9 same:True
Assign an ordinary (int,int) tuple to Window, then copy array contents before mutating them. Type naming and ownership are separate questions.