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

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

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

## Summary        {#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.

```osy syntax
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      {#signature}

```osy syntax
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    {#description}

### What text does it accept?   {#accepts}

The rules are C#'s:

| text | `Enum.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](https://osysharp.com/reference/enum/flags/)) |
| `"Shiped"`, `""` | throws `ArgumentException`: `Requested value 'Shiped' was not found in OrderState — it has: Placed, Paid, Shipped, Cancelled.` |
| `null` | throws `ArgumentNullException` |

### How is it different from C#?   {#narrower}

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?   {#tryparse}

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:

```osy syntax
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](https://osysharp.com/reference/workflow/entity-lifecycle/)).

### Where does it run?   {#where}

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       {#examples}

```osy title="reading a state from text, refusing a typo" test app=enum-parse
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);
```

```osy title="proof: a name parses, a typo is refused" run app=enum-parse
[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       {#see-also}
- [enum](https://osysharp.com/reference/enum/declaration/) — declaring an enum and how its members are stored
- [[Label], [Icon], [Tone] — what a human reads](https://osysharp.com/reference/enum/labels/) — `.Label` and `.Name`, the way back from a member to text
- [[Flags] — a value that holds several members](https://osysharp.com/reference/enum/flags/) — `[Flags]` values, which parse from `"Read, Write"`
