Skip to content

Generating code of value object by C# 9.0 Source Generator

License

Notifications You must be signed in to change notification settings

r-koubou/ValueObjectGenerator

Repository files navigation

ValueObject Generator

Generating code of value object by C# 9.0 Source Generator

Simple value objects can be created.

Example

[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 Attribute

[ValueObject <type>, [ValueName], [Option] ]

Type (*Required)

Type of value. use typeof syntax.

ValueName

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")
}

Option

Flags to specify additional value specifications.

if set anOptionFlags value to ValueObjectAttribute, Generate code according to the flag value

NonValidating

  • 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 );

Explicit

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 );
}

Implicit

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 );
}

Comparable

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 );
}

ToString

Add ToStringImpl Method for custom ToString implementarion

Default

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() ?? "";
}

Available other attribute

Provides presets for validation. In many cases, it is exclusive to the Validate method.

Value range

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;
    }
  :
  :
}

Not negative

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;
    }
  :
  :
}

Not empty

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;
    }
  :
  :
}

About

Generating code of value object by C# 9.0 Source Generator

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published