Osy#betaRun it on Osyrin: osyrin.com

Reference / Enum

Enum.Parse<T> and Enum.TryParse<T> — text into a member

T Enum.Parse<T>(string value[, bool ignoreCase]) bool Enum.TryParse<T>(string value[, bool ignoreCase], out T result)

Enum.Parse<OrderState>("Paid") turns a member's name into the member, exactly as in C#; an unknown name throws an ArgumentException that lists every member. Enum.TryParse<OrderState>(text, out var state) answers false instead of throwing. Both run in a function and in a component. Never hand-write a lookup with a fallback member — a typo then silently becomes that member.

stable2 examples compiled by CIenumparsestdlib

Summary#

Enum.Parse<T>(text) answers the member of T that text names — Enum.Parse<OrderState>("Paid") is OrderState.Paid. A name the enum does not have throws an ArgumentException naming the enum and listing every member. Enum.TryParse<T>(text, out var v) is the same parse without the throw: it answers false and leaves you to say what an unknown name means.

OrderState s = Enum.Parse<OrderState>("Paid");                 // OrderState.Paid
OrderState t = Enum.Parse<OrderState>("paid", ignoreCase: true);
if (Enum.TryParse<OrderState>(text, out var state)) { … }       // false for "Shiped"

Signature#

T    Enum.Parse<T>(string value)
T    Enum.Parse<T>(string value, bool ignoreCase)
bool Enum.TryParse<T>(string value, out T result)
bool Enum.TryParse<T>(string value, bool ignoreCase, out T result)
bool Enum.TryParse(string value, out T result)                  // T named on the `out`

Description#

What text does it accept?#

The rules are C#'s:

textEnum.Parse<OrderState>(…)
"Paid"OrderState.Paid — a member name, compared exactly
" Paid "OrderState.Paid — surrounding whitespace is trimmed
"paid"throws; with ignoreCase: true, OrderState.Paid
"1"the member whose value is 1 — text that is a number parses as a value
"Read, Write"on a [Flags] enum, the members combined — the way the value prints ([Flags] — a value that holds several members)
"Shiped", ""throws ArgumentException: Requested value 'Shiped' was not found in OrderState — it has: Placed, Paid, Shipped, Cancelled.
nullthrows ArgumentNullException

How is it different from C#?#

In one place, on purpose. C# also accepts a number no member has: Enum.Parse<OrderState>("99") is (OrderState)99 there. Osy# refuses it like any other unknown value. An enum value is stored as its member's key, and 99 would be stored as a key that no member names and no label can show. On a [Flags] enum, a number made of declared bits ("7" for Read | Write | Delete) is accepted, because the enum can hold that value.

A [Type(string)] enum stores the member's name, so it parses names only, never numbers or a comma list.

When should I use TryParse instead?#

Use Parse when the text should already be valid and a bad value is a bug, such as a value your own code wrote. Use TryParse for text from a person or another system, where an unknown name is something to report:

if (!Enum.TryParse<OrderState>(input, out var state)) {
  throw new ArgumentException($"'{input}' is not an order state.");
}
order.State = state;

Declare the variable in the call (out var state, or out OrderState state). Filling a variable that already exists is refused: on failure C# writes default(T) into it, which is the value 0, and the enum may not declare 0.

⚠ Do not write your own switch over names that falls back to a member. A typo then becomes that member without any error, for example an order that is silently cancelled. osy lint reports that shape as correctness-enum-name-with-a-silent-default. A fallback member named for not knowing (Unknown, Other, None) is an honest answer and is not reported. When the text names the next state of a row, take the enum instead of a string, or write one function per move (An entity's lifecycle — a workflow that tracks its status, or one function per move).

Where does it run?#

Everywhere an expression runs: a function on the server, and an action or render block in the browser, with no round trip. The members are known when the app compiles, so both sides give the same answer, with the same message.

Examples#

enum OrderState { Placed, Paid, [Label("On its way")] Shipped, Cancelled }

entity Order {
  [Required, MaxLength(40)] string Ref;
  OrderState State;
  security { allow read, create, update when IsAuthenticated; }
}

// Text from an import or a form: an unknown state is reported, never guessed.
string SetState(Order order, string text) {
  if (!Enum.TryParse<OrderState>(text, true, out var state)) {
    return $"'{text}' is not an order state.";
  }
  order.State = state;
  return $"Now {state.Label}.";
}

// Text the app wrote itself: a bad value is a bug, so let it throw.
OrderState Stored(string name) => Enum.Parse<OrderState>(name);
[Test]
void Parsing_names_a_member_or_refuses() {
  Assert.Equal(OrderState.Shipped, Stored("Shipped"));
  Assert.Equal(OrderState.Paid, Enum.Parse<OrderState>("paid", true));
  Assert.False(Enum.TryParse<OrderState>("Shiped", out var typo));
  Assert.Throws<ArgumentException>(() => Stored("Shiped"));
}

See also#

Related

enum

A fixed set of named values, used as a member type. Stored as a number by default, or as the member's own name with…

[Label], [Icon], [Tone] — what a human reads

An enum member stores a compact value but shows a human-readable label. [Label("…")] gives a member its label…

[Flags] — a value that holds several members

[Flags] makes an enum's members bits, so one value can hold several of them at once — Access.Read | Access.Write…

Enums

A fixed set of named values, used as a member type. By default an enum stores as a compact number; add [Type(string)]…

An entity's lifecycle — a workflow that tracks its status, or one function per move

A row that moves through states, like an order going from placed to paid to shipped, has two good shapes. A workflow…