{"id":13629352,"url":"https://github.com/mcintyre321/OneOf","last_synced_at":"2025-04-17T09:33:23.751Z","repository":{"id":39579544,"uuid":"49533213","full_name":"mcintyre321/OneOf","owner":"mcintyre321","description":"Easy to use F#-like ~discriminated~ unions for C# with exhaustive compile time matching","archived":false,"fork":false,"pushed_at":"2024-08-07T14:03:29.000Z","size":860,"stargazers_count":3698,"open_issues_count":57,"forks_count":168,"subscribers_count":49,"default_branch":"master","last_synced_at":"2025-04-09T19:04:52.226Z","etag":null,"topics":["c-sharp","discriminated-unions","dotnetcore","f-sharp"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mcintyre321.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-01-12T22:31:19.000Z","updated_at":"2025-04-09T09:42:48.000Z","dependencies_parsed_at":"2024-11-26T12:48:35.273Z","dependency_job_id":null,"html_url":"https://github.com/mcintyre321/OneOf","commit_stats":{"total_commits":137,"total_committers":30,"mean_commits":4.566666666666666,"dds":0.708029197080292,"last_synced_commit":"7cd022ccc983c26abfb6ee3649986ec8b178336a"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcintyre321%2FOneOf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcintyre321%2FOneOf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcintyre321%2FOneOf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcintyre321%2FOneOf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mcintyre321","download_url":"https://codeload.github.com/mcintyre321/OneOf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249331615,"owners_count":21252618,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-sharp","discriminated-unions","dotnetcore","f-sharp"],"created_at":"2024-08-01T22:01:08.315Z","updated_at":"2025-04-17T09:33:23.097Z","avatar_url":"https://github.com/mcintyre321.png","language":"C#","readme":"# OneOf [![NuGet](https://img.shields.io/nuget/v/OneOf?logo=nuget)](https://www.nuget.org/packages/OneOf/) [![GitHub](https://img.shields.io/github/license/mcintyre321/OneOf)](licence.md)\n\n\u003e \"Ah! It's like a compile time checked switch statement!\" - Mike Giorgaras\n\n## Getting Started\n\n\u003e `install-package OneOf`\n\nThis library provides F# style ~discriminated~ unions for C#, using a custom type `OneOf\u003cT0, ... Tn\u003e`. An instance of this type holds a single value, which is one of the types in its generic argument list.\n\nI can't encourage you enough to give it a try! Due to exhaustive matching DUs provide an alternative to polymorphism when you want to have a method with guaranteed behaviour-per-type (i.e. adding an abstract method on a base type, and then implementing that method in each type). It's a really powerful tool, ask any f#/Scala dev! :)\n\nPS If you like OneOf, you might want to check out [ValueOf](https://github.com/mcintyre321/valueof), for one-line Value Object Type definitions.\n\n## Use cases\n\n### As a method return value\n\nThe most frequent use case is as a return value, when you need to return different results from a method. Here's how you might use it in an MVC controller action:\n\n```csharp\npublic OneOf\u003cUser, InvalidName, NameTaken\u003e CreateUser(string username)\n{\n    if (!IsValid(username)) return new InvalidName();\n    var user = _repo.FindByUsername(username);\n    if(user != null) return new NameTaken();\n    var user = new User(username);\n    _repo.Save(user);\n    return user;\n}\n\n[HttpPost]\npublic IActionResult Register(string username)\n{\n    OneOf\u003cUser, InvalidName, NameTaken\u003e createUserResult = CreateUser(username);\n    return createUserResult.Match(\n        user =\u003e new RedirectResult(\"/dashboard\"),\n        invalidName =\u003e {\n            ModelState.AddModelError(nameof(username), $\"Sorry, that is not a valid username.\");\n            return View(\"Register\");\n        },\n        nameTaken =\u003e {\n            ModelState.AddModelError(nameof(username), \"Sorry, that name is already in use.\");\n            return View(\"Register\");\n        }\n    );\n}\n```\n\n#### As an 'Option' Type\n\nIt's simple to use OneOf as an `Option` type - just declare a `OneOf\u003cSomething, None\u003e`. OneOf comes with a variety of useful Types in the `OneOf.Types` namespace, including  `Yes`, `No`, `Maybe`, `Unknown`, `True`, `False`, `All`, `Some`, and `None`.\n\n#### Benefits\n\n- True strongly typed method signature\n  - No need to return a custom result base type e.g `IActionResult`, or even worse, a non-descriptive type (e.g. object)\n  - The method signature accurately describes all the potential outcomes, making it easier for consumers to understand the code\n  - Method consumer HAS to handle all cases (see 'Matching', below)\n- You can avoid using [\"Exceptions for control flow\"](http://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why) antipattern by returning custom Typed error objects\n  \n### As a method parameter value\n\nYou can use also use `OneOf` as a parameter type, allowing a caller to pass different types without requiring additional overloads. This might not seem that useful for a single parameter, but if you have multiple parameters, the number of overloads required increases rapidly.\n\n```csharp\npublic void SetBackground(OneOf\u003cstring, ColorName, Color\u003e backgroundColor) { ... }\n\n//The method above can be called with either a string, a ColorName enum value or a Color instance.\n```\n\n## Matching\n\nYou use the `TOut Match(Func\u003cT0, TOut\u003e f0, ... Func\u003cTn,TOut\u003e fn)` method to get a value out. Note how the number of handlers matches the number of generic arguments.\n\n### Advantages over `switch` or `if` or `exception` based control flow:\n\nThis has a major advantage over a switch statement, as it\n\n- requires every parameter to be handled\n- No fallback - if you add another generic parameter, you HAVE to update all the calling code to handle your changes.\n\n    In brown-field code-bases this is incredibly useful, as the default handler is often a runtime `throw NotImplementedException`, or behaviour that wouldn't suit the new result type.\n\nE.g.\n\n```csharp\nOneOf\u003cstring, ColorName, Color\u003e backgroundColor = ...;\nColor c = backgroundColor.Match(\n    str =\u003e CssHelper.GetColorFromString(str),\n    name =\u003e new Color(name),\n    col =\u003e col\n);\n_window.BackgroundColor = c;\n```\n\nThere is also a .Switch method, for when you aren't returning a value:\n\n```csharp\nOneOf\u003cstring, DateTime\u003e dateValue = ...;\ndateValue.Switch(\n    str =\u003e AddEntry(DateTime.Parse(str), foo),\n    int =\u003e AddEntry(int, foo)\n);\n```\n\n### TryPick𝑥 method\n\nAs an alternative to `.Switch` or `.Match` you can use the `.TryPick𝑥` methods.\n\n```csharp\n//TryPick𝑥 methods for OneOf\u003cT0, T1, T2\u003e\npublic bool TryPickT0(out T0 value, out OneOf\u003cT1, T2\u003e remainder) { ... }\npublic bool TryPickT1(out T1 value, out OneOf\u003cT0, T2\u003e remainder) { ... }\npublic bool TryPickT2(out T2 value, out OneOf\u003cT0, T1\u003e remainder) { ... }\n```\n\nThe return value indicates if the OneOf contains a T𝑥 or not. If so, then `value` will be set to the inner value from the OneOf. If not, then the remainder will be a OneOf of the remaining generic types. You can use them like this:\n\n```csharp\nIActionResult Get(string id)\n{\n    OneOf\u003cThing, NotFound, Error\u003e thingOrNotFoundOrError = GetThingFromDb(string id);\n\n    if (thingOrNotFoundOrError.TryPickT1(out NotFound notFound, out var thingOrError)) //thingOrError is a OneOf\u003cThing, Error\u003e\n      return StatusCode(404);\n\n    if (thingOrError.TryPickT1(out var error, out var thing)) //note that thing is a Thing rather than a OneOf\u003cThing\u003e\n    {\n      _logger.LogError(error.Message);\n      return StatusCode(500);\n    }\n\n    return Ok(thing);\n}\n```\n\n### Reusable OneOf Types using OneOfBase\n\nYou can declare a OneOf as a type, either for reuse of the type, or to provide additional members, by inheriting from `OneOfBase`. The derived class will inherit the `.Match`, `.Switch`, and `.TryPick𝑥` methods.\n\n```csharp\npublic class StringOrNumber : OneOfBase\u003cstring, int\u003e\n{\n    StringOrNumber(OneOf\u003cstring, int\u003e _) : base(_) { }\n\n    // optionally, define implicit conversions\n    // you could also make the constructor public\n    public static implicit operator StringOrNumber(string _) =\u003e new StringOrNumber(_);\n    public static implicit operator StringOrNumber(int _) =\u003e new StringOrNumber(_);\n\n    public (bool isNumber, int number) TryGetNumber() =\u003e\n        Match(\n            s =\u003e (int.TryParse(s, out var n), n),\n            i =\u003e (true, i)\n        );\n}\n\nStringOrNumber x = 5;\nConsole.WriteLine(x.TryGetNumber().number);\n// prints 5\n\nx = \"5\";\nConsole.WriteLine(x.TryGetNumber().number);\n// prints 5\n\nx = \"abcd\";\nConsole.WriteLine(x.TryGetNumber().isNumber);\n// prints False\n```\n\n### OneOfBase Source Generation \n\nYou can automatically generate `OneOfBase` hierarchies using `GenerateOneOfAttribute` and partial class that extends `OneOfBase` using\na Source Generator (thanks to @romfir for the contribution :D). Install it via\n\n\u003e Install-Package OneOf.SourceGenerator\n\nand then define a stub like so:\n\n```csharp\n[GenerateOneOf]\npublic partial class StringOrNumber : OneOfBase\u003cstring, int\u003e { }\n```\n\nDuring compilation the source generator will produce a class implementing the OneOfBase boiler plate code for you. e.g.\n\n```csharp\npublic partial class StringOrNumber\n{\n\tpublic StringOrNumber(OneOf.OneOf\u003cSystem.String, System.Int32\u003e _) : base(_) { }\n\n\tpublic static implicit operator StringOrNumber(System.String _) =\u003e new StringOrNumber(_);\n\tpublic static explicit operator System.String(StringOrNumber _) =\u003e _.AsT0;\n\n\tpublic static implicit operator StringOrNumber(System.Int32 _) =\u003e new StringOrNumber(_);\n\tpublic static explicit operator System.Int32(StringOrNumber _) =\u003e _.AsT1;\n}\n```\n","funding_links":[],"categories":["Content","Results Objects Pattern (Discriminate Unions + Functional Programming)","C#","Algorithms and Data structures","C\\#","C# #"],"sub_categories":["51. [OneOf](https://ignatandrei.github.io/RSCG_Examples/v2/docs/OneOf) , in the [FunctionalProgramming](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#functionalprogramming) category"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcintyre321%2FOneOf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcintyre321%2FOneOf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcintyre321%2FOneOf/lists"}