Generating code of value object by C# 9.0 Source Generator
Simple value objects can be created.
[ValueObject(typeof(int))]
public partial class Sample // 'struct' also supporting
{
// By default, the Validate method is defined and called from constructor
// If ValueOption.NonValidating is set, Validate method will not be defined
private static partial int Validate( int value ) => value;
}
will be generated to following
using System;
using System.Diagnostics.CodeAnalysis;
public partial struct Sample : IEquatable<Sample>
{
public int Value { get; }
public Sample( int value )
{
Value = Validate( value );
}
private static partial int Validate( int value );
//
// Default ToString()
//
public override string ToString()
{
return Value.ToString() ?? "";
}
//----------------------------------------------------------------------
// Equality
//----------------------------------------------------------------------
public bool Equals( Sample other )
{
return Equals( Value, other.Value );
}
public override bool Equals( [AllowNull] object obj )
{
return obj is Sample other && Equals( other );
}
// HashCode
public override int GetHashCode() => Value.GetHashCode();
// Operator ==, !=
public static bool operator ==( Sample a, Sample b )
{
return a.Equals( b );
}
public static bool operator !=( Sample a, Sample b )
{
return !( a == b );
}
}
[ValueObject <type>, [ValueName], [Option] ]
Type of value. use typeof syntax.
Applied to variable name (default: "Value")
e.g.
// Explicitly set the name of the value variable
[ValueObject(typeof(int), ValueName ="Point")]
public partial class Hp {}
will be generated to following
public partial class Hp : IEquatable<Hp>
{
public int Point { get; } // variable name will be "Point" (default: "Value")
}
Flags to specify additional value specifications.
if set anOptionFlags
value to ValueObjectAttribute, Generate code according to the flag value
- Don't genetate
Valid
method - Don't validate in constructor
Example
[ValueObject( typeof(int), Option = ValueOption.NonValidating)]
public partial class Sample {}
// *Validate method will not be defined
// private static partial int Validate( int value );
Add explicit operator
Example
[ValueObject( typeof(int), Option = ValueOption.Explicit )]
public partial class Sample {}
will be generated to following
public static explicit operator int( Sample x )
{
return x.Value;
}
public static explicit operator Sample( int value )
{
return new Sample( value );
}
Add implicit operator
Example
[ValueObject( typeof(int), Option = ValueOption.Implicit )]
public partial class Sample {}
will be generated to following
public static implicit operator int( Sample x )
{
return x.Value;
}
public static implicit operator Sample( int value )
{
return new Sample( value );
}
Add IComparable<T> implementation
Example
[ValueObject( typeof(int), Option = ValueOption.Implicit )]
public partial class Sample {}
will be generated to following
public int CompareTo( Sample other )
{
if( ReferenceEquals( this, other ) )
{
return 0;
}
if( ReferenceEquals( null, other ) )
{
return 1;
}
return Value.CompareTo( other.Value );
}
Add ToStringImpl Method for custom ToString implementarion
public override string ToString()
{
return Value.ToString() ?? "";
}
Example
[ValueObject( typeof(int), Option = ValueOption.ToString )]
public partial class Sample {}
will be generated to following
private partial string ToStringImpl();
public override string ToString()
{
return ToStringImpl();
}
Default
public override string ToString()
{
return Value.ToString() ?? "";
}
Provides presets for validation. In many cases, it is exclusive to the Validate method.
Example
[ValueObject(typeof(int))]
// Set an explicit range of values
[ValueRange(0, 9999)]
public partial class Count {}
will be generated to following
public partial class Count : IEquatable<Count>
{
public int Value { get; }
public Count( int value )
{
if( value < (0) || value > (9999) )
{
throw new ArgumentOutOfRangeException( $"(Count) Out of range : {value} (range:0 < 9999)" );
}
Value = value;
}
:
:
}
Example
[ValueObject(typeof(int))]
[NotNegative]
public partial class Count {}
will be generated to following
public partial class Count : IEquatable<Count>
{
public int Value { get; }
public Count( int value )
{
if( value < 0 )
{
throw new ArgumentException( $"(Count) value is negative : {value}" );
}
Value = value;
}
:
:
}
Example
[ValueObject(typeof(string))]
[NotEmpty]
public partial class Name {}
will be generated to following
public partial class Name : IEquatable<Name>
{
public string Value { get; }
public Name( string value )
{
if( string.IsNullOrEmpty( value ) || value.Trim().Length == 0 )
{
throw new ArgumentException( $"(Name) value is empty" );
}
Value = value;
}
}
Note: if type is string, use string.IsNullOrEmpty, Trim. Otherwise use Linq.Any()
e.g.
[ValueObject(typeof(string[]))]
[NotEmpty]
public partial class Names {}
will be generated to following
public partial class Names : IEquatable<Names>
{
public string[] Value { get; }
public Names( string[] value )
{
if( !value.Any() )
{
throw new ArgumentException( $"(Names) value is empty" );
}
Value = value;
}
:
:
}
If you do not want to treat a string with only whitespace characters as Empty, set the ExcludeWhiteSpace argument to true.
[ValueObject(typeof(string))]
[NotEmpty(ExcludeWhiteSpace=true)]
public partial class Name {}
will be generated to following
public partial class Name : IEquatable<Name>
{
public string Value { get; }
public Name( string value )
{
if( string.IsNullOrEmpty( value ) )
{
throw new ArgumentException( $"(Name) value is empty" );
}
Value = value;
}
:
:
}