diff --git a/Janus.Analyzers/Components/ConstructorComponent.cs b/Janus.Analyzers/Components/ConstructorComponent.cs index 70c5475..3c9eba0 100644 --- a/Janus.Analyzers/Components/ConstructorComponent.cs +++ b/Janus.Analyzers/Components/ConstructorComponent.cs @@ -35,6 +35,10 @@ public void AppendTo(CSharpSourceBuilder builder, CancellationToken cancellation { var isValid = true; Validate(value, throwIfInvalid: true, ref isValid); + if(!isValid) + { + throw new {{typeof(ArgumentException)}}($"{nameof(value)} was invalid.", nameof(value)); + } } {{Create(variant, static (v, b, ct) => diff --git a/Janus.Analyzers/Components/FactoriesComponent.cs b/Janus.Analyzers/Components/FactoriesComponent.cs index 2ee34ac..e5b136f 100644 --- a/Janus.Analyzers/Components/FactoriesComponent.cs +++ b/Janus.Analyzers/Components/FactoriesComponent.cs @@ -197,6 +197,28 @@ public static bool TryCreate( {{variant.Type.NullableName}} value) => new {{new UnionTypeNameComponent(m)}}(value, validate: true); + {{Summary(m, static (m, b, ct) => + { + ct.ThrowIfCancellationRequested(); + + b.Append($"Creates an instance of {Cref(m.DocsCommentId)}."); + })}} + {{Param("value", m, static (m, b, ct) => + { + ct.ThrowIfCancellationRequested(); + + b.Append($"The value to create an instance of {Cref(m.DocsCommentId)} from."); + })}} + {{Returns(m, static (m, b, ct) => + { + ct.ThrowIfCancellationRequested(); + + b.Append($"The new instance of {Cref(m.DocsCommentId)}."); + })}} + public static {{new UnionTypeNameComponent(m)}} CreateFrom{{variant.Name}}( + {{variant.Type.NullableName}} value) + => new {{new UnionTypeNameComponent(m)}}(value, validate: true); + {{Summary(m, static (m, b, ct) => { ct.ThrowIfCancellationRequested(); @@ -238,6 +260,48 @@ public static bool TryCreate( return isValid; } + + {{Summary(m, static (m, b, ct) => + { + ct.ThrowIfCancellationRequested(); + + b.Append($"Attempts to create an instance of {Cref(m.DocsCommentId)}."); + })}} + {{Param("value", m, static (m, b, ct) => + { + ct.ThrowIfCancellationRequested(); + + b.Append($"The value to create an instance of {Cref(m.DocsCommentId)} from."); + })}} + {{Param("union", m, static (m, b, ct) => + { + ct.ThrowIfCancellationRequested(); + + b.Append( + $""" + The instance of {Cref(m.DocsCommentId)} if one could be created; otherwise, + {(m.TypeKind is UnionTypeKind.Class ? Langword("null") : Langword("default"))}. + """); + })}} + {{Returns(m, static (m, b, ct) => + { + ct.ThrowIfCancellationRequested(); + + b.Append($"{Langword("true")} if an instance of {Cref(m.DocsCommentId)} could be created; otherwise, {Langword("false")}."); + })}} + public static bool TryCreateFrom{{variant.Name}}( + {{variant.Type.NullableName}} value, + [{{typeof(NotNullWhenAttribute)}}(true)] + out {{new UnionTypeNameComponent(m, RenderNullable: true)}} union) + { + var isValid = true; + Validate(value, throwIfInvalid: false, ref isValid); + union = isValid + ? new {{new UnionTypeNameComponent(m)}}(value, validate: false) + : default; + + return isValid; + } """ ); } diff --git a/Janus.Analyzers/Components/ToStringComponent.cs b/Janus.Analyzers/Components/ToStringComponent.cs index c8085a5..d0354e2 100644 --- a/Janus.Analyzers/Components/ToStringComponent.cs +++ b/Janus.Analyzers/Components/ToStringComponent.cs @@ -57,7 +57,7 @@ public override string ToString() b.Append( $$""" - {{p}}{(typeof({{p}}).IsGenericTypeParameter ? string.Empty : $":{typeof({{p}})}" )} + {{p}}{(typeof({{p}}) is { IsGenericParameter: true, DeclaringMethod: null } ? string.Empty : $":{typeof({{p}})}" )} """ ); }, separator: ", ")}>"); diff --git a/Janus.Analyzers/UnionTypeSettingsAttribute.cs b/Janus.Analyzers/UnionTypeSettingsAttribute.cs index ae3019e..200bb8d 100644 --- a/Janus.Analyzers/UnionTypeSettingsAttribute.cs +++ b/Janus.Analyzers/UnionTypeSettingsAttribute.cs @@ -105,6 +105,7 @@ sealed partial class UnionTypeSettingsAttribute : global::System.Attribute /// Defines how to generate an implementation for . /// public ToStringSetting ToStringSetting { get; set; } + /// /// Indicates how to generate equality operators. /// By default, equality operators will only be emitted for value types, to preserve @@ -112,7 +113,6 @@ sealed partial class UnionTypeSettingsAttribute : global::System.Attribute /// public EqualityOperatorsSetting EqualityOperatorsSetting { get; set; } - /// /// Gets or sets a value indicating whether to make the union type JSON serializable. /// diff --git a/Janus/README.md b/Janus/README.md index 7817fe3..8298295 100644 --- a/Janus/README.md +++ b/Janus/README.md @@ -1,22 +1,20 @@ -![Logo](https://raw.githubusercontent.com/PaulBraetz/RhoMicro.CodeAnalysis/master/UnionsGenerator/ReadmeLogo.svg) +![Logo](https://raw.githubusercontent.com/PaulBraetz/RhoMicro.CodeAnalysis/master/Janus/ReadmeLogo.svg) -# UnionsGenerator +# Janus +This is a source generator for generating union types. Read about union types here: https://en.wikipedia.org/wiki/Union_type ## Licensing This source code generator is licensed to you under the MPL-2.0. -The code generated by the tool, is however not subject to this license, but rather the licensing scheme of whatever project it is being used in. ## Features - generate rich examination and conversion api -- automatic relation type detection (congruency, superset, subset, intersection) - generate conversion operators - generate meaningful api names like `myUnion.IsResult` or `MyUnion.CreateFromResult(result)` -- generate the most efficient impementation for your usecase and optimize against boxing or size constraints -- group representable types and use members like `myUnion.IsNumber` +- group variants and use members like `myUnion.IsNumber` - use `System.Text.Json` serialization ## Installation @@ -24,19 +22,15 @@ The code generated by the tool, is however not subject to this license, but rath Package Reference: ``` - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + ``` + CLI: ``` -dotnet add package RhoMicro.CodeAnalysis.UnionsGenerator +dotnet add package RhoMicro.CodeAnalysis.Janus ``` -*Note: The source code generator will generate netstandard2.0 compliant code that potentially uses newer language features. It is expected that consumers enable the `11.0` flag. This could change in the future to support a wider number of language versions, however the netstandard2.0 constraint will remain. The generator generates members (e.g.: [NotNullWhenAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.notnullwhenattribute?view=netstandard-2.1) ) that rely on special types not required by netstandard2.0, therefore under some circumstances (i.e. in analyzers) a polyfill (see [PolySharp](https://www.nuget.org/packages/PolySharp/#readme-body-tab)) is required.* - ## How To Use Annotate your union type with the `UnionType` attribute: @@ -56,36 +50,42 @@ u = false; //CS0029 Cannot implicitly convert type 'bool' to 'Union' #### General Usage -Use `UnionTypeAttribute` to add `T0` to the list of representable types: +Use `UnionTypeAttribute` to add `T0` to the list of variants: ```cs [UnionType] [UnionType] partial struct IntOrString; ``` + Usage: ```cs IntOrString u = "Hello, World!"; //implicitly converted u = 32; //implicitly converted ``` -Use `UnionTypeAttribute` on type parameters to add the targeted type parameter to the list of representable types: + +Use `UnionTypeAttribute` on type parameters to add the targeted type parameter to the list of variants: ```cs partial struct GenericUnion<[UnionType] T0, [UnionType] T1>; ``` + Usage: ```cs var u = GenericUnion.CreateFromT1("Hello, World!"); u = GenericUnion.CreateFromT0(32); ``` -*Note: due to compiler restrictions no conversions from or to generic type parameters are generated. Using factory methods is an alternative way of creating union instances.* -- `Alias` +*Note: due to compiler restrictions no conversions from or to generic type parameters are generated. Using factory +methods is an alternative way of creating union instances.* + +- `Name` -Define aliae for generated members using `Alias` , e.g.: +Define names for generated members using `Name`: ```cs -[UnionType>(Alias = "MultipleNames")] -[UnionType(Alias = "SingleName")] +[UnionType>(Name = "MultipleNames")] +[UnionType(Name = "SingleName")] partial struct Names; ``` + Usage: ```cs Names n = "John"; @@ -98,139 +98,58 @@ if(n.IsSingleName) } ``` -#### `Options` - -Define miscellaneous behaviour for the represented type using `Options`. - -- `ImplicitConversionIfSolitary` - -Instructs the generator to emit an implicit conversion to the representable type if it is the only one. -In effect, this option will enable the union type to act as an alias wrapper for the representable type. - -This option is enabled by default. +- `Description` +Provide a description for the variant: ```cs -[UnionType(Options = UnionTypeOptions.ImplicitConversionIfSolitary)] -partial struct Int32Alias; +[UnionType(Description = "This variant is used for integer values.")] +partial struct Result ``` -Usage: -```cs -var i = 32; -Int32Alias u = i; -i = u; -``` - -- `Nullable` -Instructs the generator to treat the representable reference type -as nullable, allowing for `null` arguments in factories, conversions etc. +- `IsNullable` +Specify the nullability of a reference type variant: ```cs -[UnionType(Options = UnionTypeOptions.Nullable)] -[UnionType>] +NullableStringUnion foo1 = "value"; +string value1 = foo1.CastToString; // CS8600 Converting null literal or possible null value to non-nullable type. + +NonNullableStringUnion foo2 = "value"; +string value2 = foo2.CastToString; // no nullability warning + +[UnionType(IsNullable = true)] partial struct NullableStringUnion; + +[UnionType] // implicitly non-nullable +partial struct NonNullableStringUnion; ``` -Usage: -```cs -NullableStringUnion u = (String?)null; -u = new List(); -u = "Nonnull String"; -u = (List?)null; //CS8604 - Possible null reference argument for parameter. -``` -#### `Storage` - -Optimize the generated storage implementation for the representable type against boxing or size constraints using `Storage`. - -- `Auto` -> The generator will automatically decide on a storage strategy. -> -> If the representable type is known to be a value type, -> this will store values of that type inside a shared value type container. -> Boxing will not occur. -> -> If the representable type is known to be a reference type, -> this will store values of that type inside a shared reference type container. -> -> If the representable type is neither known to be a reference type -> nor a value type, this option will cause values of that type to -> be stored inside a shared reference type container. -> If the representable type is a generic type parameter, -> boxing will occur for value type arguments to that parameter. - -- `Reference` -> The generator will always store values of the representable type -> inside a shared reference type container. -> -> If the representable type is known to be a value type, -> boxing will occur. -> -> If the representable type is a generic type parameter, -> boxing will occur for value type arguments to that parameter. - -- `Value` -> The generator will attempt to store values of the representable type -> inside a value type container. -> -> If the representable type is known to be a value type, -> this will store values of that type inside a shared value type container. -> Boxing will not occur. -> -> If the representable type is known to be a reference type, -> this will store values of that type inside a shared reference type container. -> Boxing will not occur. -> -> If the representable type is neither known to be a reference type -> nor a value type, this option will cause values of that type to -> be stored inside a shared value type container. -> If the representable type is a generic type parameter, -> an exception of type TypeLoadException will occur for -> reference type arguments to that parameter. - -- `Field` -> The generator will attempt to store values of the representable type -> inside a dedicated container for that type. -> -> If the representable type is known to be a value type, -> this will store values of that type inside a dedicated -> value type container. -> Boxing will not occur. -> -> If the representable type is known to be a reference type, -> this will store values of that type inside a -> dedicated reference type container. -> -> If the representable type is neither known to be a reference type -> nor a value type, this option will cause values of that type to -> be stored inside a dedicated strongly typed container. -> Boxing will not occur. - -#### `Groups` - -Group representable types into categories by assigning `Groups`: +- `Groups` + +Group variants into categories by assigning `Groups`: ```cs [UnionType(Groups = ["Number"])] [UnionType(Groups = ["Text"])] partial struct GroupedUnion; ``` + Usage: ```cs GroupedUnion u = "Hello, World!"; -if(u.IsNumberGroup) +if(u.Variant.Groups.ContainsNumber) { Assert.Fail("Expected union to be text."); } -if(!u.IsTextGroup) +if(!u.Variant.Groups.ContainsText) { Assert.Fail("Expected union to be text."); } u = 32f; -if(!u.IsNumberGroup) +if(!u.Variant.Groups.ContainsNumber) { Assert.Fail("Expected union to be number."); } -if(u.IsTextGroup) +if(u.Variant.Groups.ContainsText) { Assert.Fail("Expected union to be number."); } @@ -238,185 +157,88 @@ if(u.IsTextGroup) ### `UnionTypeAttribute` -The generic `UnionTypeAttribute` types allow to define multiple representable types inline. Except for `Alias`, they support all of the features that `UnionTypeAttribute` and `UnionTypeAttribute` provide. Any such features will be applied to all representable types listed in the type arguments list. - -For sample usage, see [Groups](#groups). - +The generic `UnionTypeAttribute` types allow to define multiple variants inline: +```cs +[UnionType] +partial struct Union; +``` ### `UnionTypeSettingsAttribute` -Use the `UnionTypeSettingsAttribute` to supply additional instructions to the generator. The attribute may be applied to either an assembly or a union type. When targeting a union type, it defines settings specific to that type. If, however, the attribute is annotating an assembly, it supplies the default settings for every union type in that assembly. +Use the `UnionTypeSettingsAttribute` to supply additional instructions to the generator. +The attribute may be applied to either an assembly or a union type. +When targeting a union type, it defines settings specific to that type. +If, however, the attribute is annotating an assembly, it supplies the default settings for every union type in that +assembly. Settings inheritance is therefore ordered like so: -- default settings are applied (see [UnionTypeSettingsAttribute](https://raw.githubusercontent.com/PaulBraetz/RhoMicro.CodeAnalysis/master/UnionsGenerator/Attributes/UnionTypeSettingsAttribute.cs)) -- if settings could be located on assembly, settings defined therein are applied and override default settings -- if settings could be located on union type, settings defined therein are applied and override default or assembly settings - -#### `ConstructorAccessibility` - -Define the accessibility of generated constructors: - -- `PublicIfInconvertible` ->Generated constructors should always be private, unless ->no conversion operators are generated for the type they ->accept. This would be the case for interface types or ->supertypes of the target union. - -- `Private` -> Generated constructors should always be private. - -- `Public` -> Generated constructors should always be public. - -#### `DiagnosticsLevel` - -Define the reporting of diagnostics: - -- `Info` -> Instructs the analyzer to report info diagnostics. - -- `Warning` -> Instructs the analyzer to report warning diagnostics. - -- `Error` -> Instructs the analyzer to report error diagnostics. - -- `All` -> Instructs the analyzer to report all diagnostics. +- default settings are applied ( + see [UnionTypeSettingsAttribute](https://raw.githubusercontent.com/PaulBraetz/RhoMicro.CodeAnalysis/master/Janus/Attributes/UnionTypeSettingsAttribute.cs)) +- if settings could be located on assembly, settings defined therein are applied and override inherited settings +- if settings could be located on union type, settings defined therein are applied and override inherited or assembly + settings #### `ToStringSetting` Define how implementations of `ToString` should be generated: +- `Inherit` +> Inherits the setting. This is the default value. +> - If the target is a type, it will inherit the setting from its containing assembly. +> - If the target is an assembly, the `Detailed` setting will be used. + - `Detailed` > The generator will emit an implementation that returns detailed information, including: > - the name of the union type -> - a list of types representable by the union type -> - an indication of which type is being represented by the instance +> - the set of variants +> - an indication of which variant is being represented by the instance > - the value currently being represented by the instance - `None` -> The generator will not generate an implementation of ToString. +> The generator will not generate an implementation of `ToString`. - `Simple` > The generator will generate an implementation that returns the result of -> calling ToString on the currently represented value. - -#### `Layout` - -Generate a layout attribute for size optimization: - -- `Small` -> Generate an annotation optimized for size. - -- `Auto` -> Do not generate any annotations. - -#### Identifiers - -Define various identifiers used in the generated implementation: -- `TypeDeclarationPreface` -> A raw code preface to prepend before the generated type declaration. -- `GenericTValueName` -> The name of the generic parameter for generic Is, As and factory methods. -> Set this property in order to avoid name collisions with generic union type parameters -- `TryConvertTypeName` -> The name of the generic parameter for the TryConvert method. -> Set this property in order to avoid name collisions with generic union type parameters -- `MatchTypeName` -> The name of the generic parameter for the Match method. -> Set this property in order to avoid name collisions with generic union type parameters -- `TagTypeName` -> The name to use for the discriminating tag type. -- `ValueTypeContainerTypeName` -> The name to use for the container type containing value types. -- `ValueTypeContainerName` -> The name to use for the field containing value types. -- `ReferenceTypeContainerName` -> The name to use for the field containing reference types. -- `TagFieldName` -> The name to use for the field containing the discriminating tag. -- `TagNoneName` -> The name to use for the default (uninitialized) tag value. -- `JsonConverterTypeName` -> The name of the generated json converter type. - -### `RelationAttribute` - -This attribute defines a relation between the targeted union type the supplied type. The following relations are available: -- `Disjunct` -- `Congruent` -- `Superset` -- `Subset` -- `Intersection` - -The generator will automatically detect the relation between two union types. The only requirement is for one of the two types to be annotated with the `RelationAttribute`: -```cs -[UnionType] -[Relation] -readonly partial struct Union; +> calling `ToString` on the currently represented value. -[UnionType] -sealed partial class CongruentUnion; +#### `EqualityOperatorsSetting` -[UnionType] -partial class SubsetUnion; +Define if equality operators should be generated: -[UnionType] -partial struct SupersetUnion; +- `Inherit` +> Inherits the setting. This is the default value. +> - If the target is a type, it will inherit the setting from its containing assembly. +> - If the target is an assembly, the `EmitOperatorsIfValueType` setting will be used. -[UnionType>] -partial class IntersectionUnion; -``` +- `EmitOperatorsIfValueType` +> Equality operators will be emitted only if the target union type is a value type. + +- `EmitOperators` +> Equality operators will be emitted. -Upon detecting a union relation, the generator will emit conversion operators approriate to the inferred relation type: - -- `Disjunct` -> There is no relation between the provided type and target type. -> They do not share any representable types. -> No conversion operators will be generated. - -- `BidirectionalRelation` -> The relation is defined on both the target type as well as the provided type. -> Only for one of the two union types will conversion operators be generated. - -- `Superset` -> The target type is a superset of the provided type. -> The target type may represent all of the provided types representable types. -> This means that two conversion operations will be generated: -> - an implicit conversion operator from the provided type to the target type -> - an explicit conversion operator from the target type to the provided type -> This option is not available if the provided type has already defined a relation to the target type. - -- `Subset` -> The target type is a subset of the provided type. -> The provided type may represent all of the target types representable types. -> This means that two conversion operations will be generated: -> - an implicit conversion operator from the target type to the provided type -> - an explicit conversion operator from the provided type to the target type -> This option is not available if the provided type has already defined a relation to the target type. - -- `Intersection` -> The target type intersects the provided type. -> The target type may represent some, but not all of the provided types representable types; and vice-versa. -> This means that two conversion operations will be generated: -> - an explicit conversion operator from the target type to the provided type -> - an explicit conversion operator from the provided type to the target type -> This option is not available if the provided type has already defined a relation to the target type. - -- `Congruent` -> The target type is congruent to the provided type. -> The target type may represent all of the provided types representable types; and vice-versa. -> This means that two conversion operations will be generated: -> - an implicit conversion operator from the target type to the provided type -> - an implicit conversion operator from the provided type to the target type -> This option is not available if the provided type has already defined a relation to the target type. - -## Contrived Example - -In our imaginary usecase, a user shall be retrieved from the infrastructure via a name query. The following types will be found throughout the example: +- `OmitOperators` +> Equality operators will be omitted. +#### `JsonConverterSetting` + +Define how JSON support should be generated: + +- `Inherit` +> Inherits the setting. This is the default value. +> - If the target is a type, it will inherit the setting from its containing assembly. +> - If the target is an assembly, the `OmitJsonConverter` setting will be used. + +- `OmitJsonConverter` +> No JSON converter implementation is emitted. + +- `EmitJsonConverter` +> A JSON converter implementation is emitted. + +## Compound Example + +In our imaginary usecase, a user shall be retrieved from the infrastructure via a name query. The following types will +be found throughout the example: ```cs sealed record User(String Name); @@ -429,14 +251,16 @@ enum ErrorCode readonly record struct MultipleUsersError(Int32 Count); ``` -The `User` type represents a user. The `ErrorCode` represents an error that does not contain additional information, like `MultipleUsersError` does. It represents multiple users having been found while only one was requested. +The `User` type represents a user. The `ErrorCode` represents an error that does not contain additional information, +like `MultipleUsersError` does. It represents multiple users having been found while only one was requested. We define a union type to represent our imaginary query: - ```cs -[UnionType] +[UnionType(Groups = ["Error"])] +[UnionType(Groups = ["Success"])] readonly partial struct GetUserResult; ``` + Instances of `GetUserResult` can represent *either* an instance of `ErrorCode`, `MultipleUsersError` or `User`. It will be used in a service façade like so: @@ -446,6 +270,7 @@ interface IUserService GetUserResult GetUserByName(String name); } ``` + A repository abstracts over the underlying infrastructure: ```cs interface IUserRepository @@ -453,10 +278,12 @@ interface IUserRepository IQueryable UsersByName(String name); } ``` + Access violations would be communicated through the repository using the following exception type: ```cs sealed class UnauthorizedDatabaseAccessException : Exception; ``` + An implementation of the `IUserService` is provided as follows: ```cs sealed class UserService : IUserService @@ -489,10 +316,12 @@ sealed class UserService : IUserService } } ``` -As you can see, possible representations of `GetUserResult` are implicitly converted and returned by the service. Users of `OneOf` will be familiar with this. -On the consumer side of this api, a generated `Match` function helps with transforming the union instance to another type: +As you can see, possible representations of `GetUserResult` are implicitly converted and returned by the service. Users +of `OneOf` will be familiar with this. +On the consumer side of this api, a generated `Match` function helps with transforming the union instance to another +type: ```cs sealed class UserModel { @@ -505,10 +334,10 @@ sealed class UserModel public void SetUser(String name) { var getUserResult = _service.GetUserByName(name); - User = getUserResult.Match( - HandleErrorCode, - HandleMultipleResult, - user => user); + User = getUserResult.Switch( + onErrorCode: HandleErrorCode, + onMultipleUsersError: HandleMultipleResult, + onUser: user => user); } private User? HandleErrorCode(ErrorCode code) { @@ -527,321 +356,3 @@ sealed class UserModel } } ``` - -Here is a list of some generated members on the `GetUserResult` union type (implementations and some details have been elided): -```cs -/// -/// Creates a new instance of representing an instance of . -/// -private GetUserResult(ErrorCode value) - -/// -/// Creates a new instance of representing an instance of . -/// -private GetUserResult(MultipleUsersError value) - -/// -/// Creates a new instance of representing an instance of . -/// -private GetUserResult(User value) - -/// -/// Creates a new instance of representing an instance of . -/// -/// -/// The value to be represented by the new instance of . -/// -/// -/// A new instance of representing . -/// -public static GetUserResult CreateFromErrorCode([RhoMicro.CodeAnalysis.UnionTypeFactory]ErrorCode value) - -/// -/// Creates a new instance of representing an instance of . -/// -/// -/// The value to be represented by the new instance of . -/// -/// -/// A new instance of representing . -/// -public static GetUserResult CreateFromMultipleUsersError([RhoMicro.CodeAnalysis.UnionTypeFactory]MultipleUsersError value) - -/// -/// Creates a new instance of representing an instance of . -/// -/// -/// The value to be represented by the new instance of . -/// -/// -/// A new instance of representing . -/// -public static GetUserResult CreateFromUser([RhoMicro.CodeAnalysis.UnionTypeFactory]User value) - -/// -/// Attempts to create an instance of from an instance of . -/// -/// -/// The value from which to attempt to create an instance of . -/// -/// -/// If an instance of could successfully be created, this parameter will contain the newly created instance; otherwise, . -/// -/// -/// if an instance of could successfully be created; otherwise, . -/// -public static System.Boolean TryCreate(TValue value, out GetUserResult result) - -/// -/// Creates an instance of from an instance of . -/// -/// -/// The value from which to create an instance of . -/// -/// -/// A new instance of representing . -/// -public static GetUserResult Create(TValue value) - -/// -/// Invokes a handler based on the type of value being represented. -/// -/// -/// The handler to invoke if the union is currently representing an instance of . -/// -/// -/// The handler to invoke if the union is currently representing an instance of . -/// -/// -/// The handler to invoke if the union is currently representing an instance of . -/// -public void Switch( - System.Action onErrorCode, - System.Action onMultipleUsersError, - System.Action onUser) - -/// -/// Invokes a projection based on the type of value being represented. -/// -/// -/// The projection to invoke if the union is currently representing an instance of . -/// -/// -/// The projection to invoke if the union is currently representing an instance of . -/// -/// -/// The projection to invoke if the union is currently representing an instance of . -/// -/// -/// The type of value produced by the projections passed. -/// -/// -/// The projected value. -/// -public TMatchResult Match( - System.Func onErrorCode, - System.Func onMultipleUsersError, - System.Func onUser) - -/// -/// Gets the types of value this union type can represent. -/// -public static System.Collections.Generic.IReadOnlyCollection RepresentableTypes { get; } - -/// -/// Gets the type of value represented by this instance. -/// -public System.Type RepresentedType - - -/// -/// Gets a value indicating whether this instance is representing a value of type . -/// -public System.Boolean IsErrorCode - -/// -/// Gets a value indicating whether this instance is representing a value of type . -/// -public System.Boolean IsMultipleUsersError - -/// -/// Gets a value indicating whether this instance is representing a value of type . -/// -public System.Boolean IsUser - -/// -/// Retrieves the value represented by this instance as a . -/// -public ErrorCode AsErrorCode - -/// -/// Retrieves the value represented by this instance as a . -/// -public MultipleUsersError AsMultipleUsersError - -/// -/// Retrieves the value represented by this instance as a . -/// -public User? AsUser - -/// -/// Determines whether this instance is representing a value of type . -/// -/// -/// if this instance is representing a value of type ; otherwise, . -/// -/// -/// If this instance is representing a value of type , this parameter will contain that value; otherwise, . -/// -public System.Boolean TryAsErrorCode( out ErrorCode value) - -/// -/// Determines whether this instance is representing a value of type . -/// -/// -/// if this instance is representing a value of type ; otherwise, . -/// -/// -/// If this instance is representing a value of type , this parameter will contain that value; otherwise, . -/// -public System.Boolean TryAsMultipleUsersError( out MultipleUsersError value) - -/// -/// Determines whether this instance is representing a value of type . -/// -/// -/// if this instance is representing a value of type ; otherwise, . -/// -/// -/// If this instance is representing a value of type , this parameter will contain that value; otherwise, . -/// -public System.Boolean TryAsUser([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out User value) - -/// -/// Determines whether this instance is representing a value of type . -/// -/// -/// The type whose representation in this instance to determine. -/// -/// -/// if this instance is representing a value of type ; otherwise, . -/// -public System.Boolean Is() - -/// -/// Determines whether this instance is representing a value of type . -/// -/// -/// If this instance is representing a value of type , this parameter will contain that value; otherwise, . -/// -/// -/// The type whose representation in this instance to determine. -/// -/// -/// if this instance is representing a value of type ; otherwise, . -/// -public System.Boolean Is(out TValue? value) - -/// -/// Determines whether this instance is representing an instance of . -/// -/// -/// The type whose representation in this instance to determine. -/// -/// -/// if this instance is representing an instance of ; otherwise, . -/// -public System.Boolean Is(System.Type type) - -/// -/// Retrieves the value represented by this instance as an instance of . -/// -/// -/// The type to retrieve the represented value as. -/// -/// -/// The currently represented value as an instance of . -/// -public TValue As() - -/// -public override System.String ToString() - -/// -public override System.Int32 GetHashCode() - -/// -public override System.Boolean Equals(System.Object? obj) - -/// -public System.Boolean Equals(GetUserResult other) - -public static System.Boolean operator ==(GetUserResult a, GetUserResult b) => a.Equals(b); -public static System.Boolean operator !=(GetUserResult a, GetUserResult b) => !a.Equals(b); - - /// -/// Converts an instance of the representable type to the union type . -/// -/// -/// The value to convert. -/// -/// -/// The union type instance. -/// -public static implicit operator GetUserResult(ErrorCode value) - -/// -/// Converts an instance of the union type to the representable type . -/// -/// -/// The union to convert. -/// -/// -/// The represented value. -/// -public static explicit operator ErrorCode(GetUserResult union) - -/// -/// Converts an instance of the representable type to the union type . -/// -/// -/// The value to convert. -/// -/// -/// The union type instance. -/// -public static implicit operator GetUserResult(MultipleUsersError value) - -/// -/// Converts an instance of the union type to the representable type . -/// -/// -/// The union to convert. -/// -/// -/// The represented value. -/// -public static explicit operator MultipleUsersError(GetUserResult union) - -/// -/// Converts an instance of the representable type to the union type . -/// -/// -/// The value to convert. -/// -/// -/// The union type instance. -/// -public static implicit operator GetUserResult(User value) - -/// -/// Converts an instance of the union type to the representable type . -/// -/// -/// The union to convert. -/// -/// -/// The represented value. -/// -public static explicit operator User(GetUserResult union) -```