Attributes Reference
Complete reference for all attributes provided by UnionGenerator.
π Available Attributesβ
UnionGenerator currently provides one core attribute:
[GenerateUnion]- Marks a class for union type generation
Future versions may include additional attributes for customization.
π·οΈ GenerateUnion Attributeβ
The [GenerateUnion] attribute is the core of UnionGenerator. Apply it to a partial class to generate a discriminated union type.
Namespaceβ
using UnionGenerator.Attributes;
Definitionβ
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class GenerateUnionAttribute : Attribute
{
}
Basic Usageβ
using UnionGenerator.Attributes;
[GenerateUnion]
public partial class Result<T, TError>
{
public static partial Result<T, TError> Ok(T value);
public static partial Result<T, TError> Error(TError error);
}
π― Attribute Targetβ
Valid Targetsβ
- Classes only: Must be applied to a
classdeclaration - Partial required: The class must be marked
partial - One per class: Cannot apply multiple times to the same class
Invalid Targetsβ
// β Not a partial class
[GenerateUnion]
public class MyUnion { } // Compilation error
// β Applied to struct
[GenerateUnion]
public partial struct MyUnion { } // Compilation error
// β Applied to interface
[GenerateUnion]
public interface IMyUnion { } // Compilation error
// β Applied to record
[GenerateUnion]
public partial record MyUnion { } // Compilation error
Valid Examplesβ
// β
Simple class
[GenerateUnion]
public partial class SimpleUnion
{
public static partial SimpleUnion CaseA();
public static partial SimpleUnion CaseB(string value);
}
// β
Generic class
[GenerateUnion]
public partial class GenericUnion<T>
{
public static partial GenericUnion<T> Some(T value);
public static partial GenericUnion<T> None();
}
// β
Nested class
public class Container
{
[GenerateUnion]
public partial class NestedUnion
{
public static partial NestedUnion Success();
public static partial NestedUnion Failure();
}
}
// β
With generic constraints
[GenerateUnion]
public partial class ConstrainedUnion<T> where T : class
{
public static partial ConstrainedUnion<T> Value(T item);
public static partial ConstrainedUnion<T> Empty();
}
π Requirementsβ
Class Requirementsβ
For the attribute to work correctly, your class must meet these requirements:
1. Must be Partialβ
// β
Correct
[GenerateUnion]
public partial class MyUnion { }
// β Wrong - Missing partial keyword
[GenerateUnion]
public class MyUnion { }
Why: The source generator adds members to your class. The partial keyword allows this.
2. Must Have Static Partial Methodsβ
[GenerateUnion]
public partial class MyUnion
{
// β
Correct - static partial
public static partial MyUnion Success(int value);
// β Wrong - not static
public partial MyUnion Failed();
// β Wrong - not partial
public static MyUnion Pending();
}
Why: Each method becomes a factory method for creating union instances.
3. Methods Must Return the Union Typeβ
[GenerateUnion]
public partial class Result<T>
{
// β
Correct - returns Result<T>
public static partial Result<T> Ok(T value);
// β Wrong - returns void
public static partial void Error(string message);
// β Wrong - returns wrong type
public static partial string Failed(string reason);
}
4. At Least One Case Requiredβ
// β Wrong - No cases defined
[GenerateUnion]
public partial class EmptyUnion
{
}
// β
Correct - At least one case
[GenerateUnion]
public partial class ValidUnion
{
public static partial ValidUnion SingleCase();
}
Parameter Requirementsβ
Case factory methods can have:
- Zero or more parameters
- Any parameter types (including generics, nullable, etc.)
- Any parameter names (used as field names in generated code)
[GenerateUnion]
public partial class FlexibleUnion<T>
{
// No parameters
public static partial FlexibleUnion<T> Empty();
// Single parameter
public static partial FlexibleUnion<T> Single(T value);
// Multiple parameters
public static partial FlexibleUnion<T> Multiple(T value, string name, int count);
// Generic parameters
public static partial FlexibleUnion<T> Generic<TData>(TData data);
// Nullable parameters
public static partial FlexibleUnion<T> Nullable(T? value);
// Complex types
public static partial FlexibleUnion<T> Complex(List<T> items, Dictionary<string, int> map);
}
π§ Advanced Scenariosβ
Inheritanceβ
Union classes can inherit from other classes or implement interfaces:
public interface IValidationResult
{
bool IsValid { get; }
}
[GenerateUnion]
public partial class ValidationResult : IValidationResult
{
public static partial ValidationResult Success();
public static partial ValidationResult Failed(string error);
// Must implement interface members
public bool IsValid => IsSuccess;
}
Note: You cannot inherit from another union type.
Multiple Generic Parametersβ
[GenerateUnion]
public partial class BiResult<TOk, TError, TWarning>
{
public static partial BiResult<TOk, TError, TWarning> Ok(TOk value);
public static partial BiResult<TOk, TError, TWarning> Error(TError error);
public static partial BiResult<TOk, TError, TWarning> Warning(TWarning warning);
}
Generic Constraintsβ
All generic constraints are preserved:
[GenerateUnion]
public partial class ConstrainedResult<T, TError>
where T : IComparable<T>
where TError : Exception, new()
{
public static partial ConstrainedResult<T, TError> Ok(T value);
public static partial ConstrainedResult<T, TError> Error(TError error);
}
Nested Genericsβ
[GenerateUnion]
public partial class NestedGeneric<T>
{
// Generic case method
public static partial NestedGeneric<T> Wrapped<TInner>(TInner value);
// Using outer generic
public static partial NestedGeneric<T> Direct(T value);
}
Nullable Reference Typesβ
UnionGenerator respects nullable reference type annotations:
#nullable enable
[GenerateUnion]
public partial class NullableAwareUnion
{
// Non-nullable string
public static partial NullableAwareUnion Valid(string name);
// Nullable string
public static partial NullableAwareUnion Maybe(string? name);
// Non-nullable with nullable generic
public static partial NullableAwareUnion GenericNullable<T>(T? value) where T : class;
}
#nullable restore
π¨ Naming Conventionsβ
Case Namesβ
Case names (method names) should be:
- PascalCase:
Success,Error,NotFound - Descriptive: Clearly indicate what the case represents
- Concise: Avoid overly long names
// β
Good case names
[GenerateUnion]
public partial class HttpResult<T>
{
public static partial HttpResult<T> Success(T data);
public static partial HttpResult<T> NotFound();
public static partial HttpResult<T> Unauthorized(string message);
public static partial HttpResult<T> ServerError(Exception exception);
}
// β Poor case names
[GenerateUnion]
public partial class BadNaming<T>
{
public static partial BadNaming<T> case1(T data); // Not PascalCase
public static partial BadNaming<T> NOTFOUND(); // All caps
public static partial BadNaming<T> TheUnauthorizedCaseWhereTheUserDoesNotHavePermission(); // Too long
}
Parameter Namesβ
Parameter names become field names in generated case classes:
- camelCase:
value,errorMessage,userId - Meaningful: Describe what the parameter represents
[GenerateUnion]
public partial class User
{
// β
Good parameter names
public static partial User Active(string userId, string email);
public static partial User Suspended(string reason, DateTime suspendedAt);
// β Poor parameter names
public static partial User Created(string s, string e); // Not descriptive
public static partial User Deleted(string Message); // Should be camelCase
}
β οΈ Common Mistakesβ
Mistake 1: Forgetting Partial Keywordβ
// β Error: Class not partial
[GenerateUnion]
public class MyUnion
{
public static partial MyUnion Case1();
}
Fix:
[GenerateUnion]
public partial class MyUnion
{
public static partial MyUnion Case1();
}
Mistake 2: Non-Static Methodβ
[GenerateUnion]
public partial class MyUnion
{
// β Error: Not static
public partial MyUnion Case1();
}
Fix:
[GenerateUnion]
public partial class MyUnion
{
public static partial MyUnion Case1();
}
Mistake 3: Wrong Return Typeβ
[GenerateUnion]
public partial class Result
{
// β Error: Returns void instead of Result
public static partial void Success();
}
Fix:
[GenerateUnion]
public partial class Result
{
public static partial Result Success();
}
Mistake 4: No Cases Definedβ
// β Error: No cases
[GenerateUnion]
public partial class EmptyUnion
{
}
Fix:
[GenerateUnion]
public partial class ValidUnion
{
public static partial ValidUnion DefaultCase();
}
π§ͺ Compiler Integrationβ
Design-Time Experienceβ
When you add [GenerateUnion]:
- β IntelliSense shows generated members immediately
- β Code completion works for pattern matching
- β Errors are shown inline in IDE
- β Go to Definition works for generated code
Build-Time Behaviorβ
During compilation:
- Source generator scans for
[GenerateUnion]attributes - Validates class structure and method declarations
- Generates implementation code
- Code is compiled together with your source
Error Messagesβ
The generator provides helpful error messages:
// Example: Missing partial keyword
[GenerateUnion]
public class BadUnion { }
// Error: UG001: Union type 'BadUnion' must be declared as partial
Common diagnostic IDs:
- UG001: Class is not partial
- UG002: No valid case methods found
- UG003: Method is not static or partial
- UG004: Method return type doesn't match class type
π Inspecting Generated Codeβ
View Generated Filesβ
In Visual Studio / Rider:
- Expand project in Solution Explorer
- Find "Dependencies" β "Analyzers" β "UnionGenerator"
- View generated
.g.csfiles
Example Generated Codeβ
For this input:
[GenerateUnion]
public partial class Option<T>
{
public static partial Option<T> Some(T value);
public static partial Option<T> None();
}
UnionGenerator creates (simplified):
public partial class Option<T>
{
// Factory implementations
public static Option<T> Some(T value) => new SomeCase(value);
public static Option<T> None() => new NoneCase();
// Case classes
public sealed class SomeCase : Option<T> { /* ... */ }
public sealed class NoneCase : Option<T> { /* ... */ }
// Pattern matching
public TResult Match<TResult>(
Func<T, TResult> some,
Func<TResult> none) { /* ... */ }
// Type checks
public bool IsSome => this is SomeCase;
public bool IsNone => this is NoneCase;
// And more...
}
π Additional Resourcesβ
- Union Generation Guide - Detailed guide on defining unions
- Generated API Reference - Complete API documentation
- Best Practices - Design recommendations
π Next Stepsβ
- Learn about Generated APIs to see what the attribute creates
- Check Extension Methods for additional helpers
- Review Pattern Matching for usage patterns