/ValueObjectGenerator

ValueObjectGenerator is Generator for ValueObjects.

Primary LanguageC#MIT LicenseMIT

ValueObjectGenerator

ValueObjectGenerator is C# source generator is for ValueObjects (ie.Wrapper classes).

This project is under the developšŸš§.

Install

Under the develop.

Usage

using ValueObjectGenerator;

[IntValueObject]
public partial class ProductId
{
}

With IntValueObject attribute, below code is generated.

public partial class ProductId: IEquatable<ProductId>
{
    public int Value { get; }

    public ProductId(int value)
    {
        Value = value;
    }

    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is ProductId other && Equals(other);
    public override int GetHashCode() => Value.GetHashCode();
    public override string ToString() => Value.ToString();
    public static bool operator ==(ProductId left, ProductId right) => Equals(left, right);
    public static bool operator !=(ProductId left, ProductId right) => !Equals(left, right);

    public bool Equals(ProductId other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Value == other.Value;
    }

    public static explicit operator ProductId(int value) => new ProductId(value);
    public static explicit operator int(ProductId value) => value.Value;
}

ProductId is a ValueObject class (ie.Wrapper classes).

Below table shows all supporting Attributes and corresponding Types.

attribute type
StringValueObject string
IntValueObject int
LongValueObject long
FloatValueObject float
DoubleValueObject double

Motivation

Next Product class has 2 int type properties, ProductId and ProductCategoryId. Some methods need ProductId as an argument, the other methods need ProductCategoryId as an argument. Sometime, developers may mistake between ProductId and ProductCategoryId.

public class Product
{
    public Product(string name, int productId, int productCategoryId)
    {
        Name = name;
        ProductId = productId;
        ProductCategoryId = productCategoryId;
    }

    public string Name { get; }
    public int ProductId { get; }
    public int ProductCategoryId { get; }
}

To avoid this mistake, we should create ProductId class and CategoryId class, and use them. With these classes, compiler can detect wrong usage of ProductId property and ProductCategoryId property.

public sealed class ProductId: IEquatable<ProductId>
{
    public int Value { get; }

    public ProductId(int value)
    {
        Value = value;
    }

    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is ProductId other && Equals(other);
    public override int GetHashCode() => Value.GetHashCode();
    public override string ToString() => Value.ToString();
    public static bool operator ==(ProductId left, ProductId right) => Equals(left, right);
    public static bool operator !=(ProductId left, ProductId right) => !Equals(left, right);

    public bool Equals(ProductId other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Value == other.Value;
    }

    public static explicit operator ProductId(int value) => new ProductId(value);
    public static explicit operator int(ProductId value) => value.Value;
}

public class CategroyId: IEquatable<CategroyId>
{
    public int Value { get; }

    public CategroyId(int value)
    {
        Value = value;
    }

    public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is CategroyId other && Equals(other);
    public override int GetHashCode() => Value.GetHashCode();
    public override string ToString() => Value.ToString();
    public static bool operator ==(CategroyId left, CategroyId right) => Equals(left, right);
    public static bool operator !=(CategroyId left, CategroyId right) => !Equals(left, right);

    public bool Equals(CategroyId other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Value == other.Value;
    }

    public static explicit operator CategroyId(int value) => new CategroyId(value);
    public static explicit operator int(CategroyId value) => value.Value;
}


public class Product
{
    public Product(string name, ProductId productId, CategroyId productCategoryId)
    {
        Name = name;
        ProductId = productId;
        ProductCategoryId = productCategoryId;
    }

    public string Name { get; }
    public ProductId ProductId { get; }
    public CategroyId ProductCategoryId { get; }
}

There are many boilerplate code in ProductId and CategoryId classes. These boilerplate code is so noisy to read other important meaningful code.

C# Source Generator can generate these boilerplate code with smart way. ValueObjectGenerator generate ValueObject classes (ie.Wrapper classes) with C# Source Generator.

Below code is example of ValueObjectGenerator. There are no boilerplate code to create ValueObjects (ie.Wrapper classes ) like ProductId and CategoryId classes.

[StringValueObject]
public class ProductName { }

[IntValueObject]
public class ProductId { }

[IntValueObject]
public class CategoryId { }

public class Product
{
    public Product(ProductName name, ProductId productId, CategoryId productCategoryId)
    {
        Name = name;
        ProductId = productId;
        ProductCategoryId = productCategoryId;
    }

    public ProductName Name { get; }
    public ProductId ProductId { get; }
    public CategoryId ProductCategoryId { get; }
}

Plans

  • IComparable support.
  • JSON serializer/deserializer.
  • other value types.
  • arithmetic operators support.

Author

Ryota Murohoshi is game Programmer in Japan.

License

This library is under MIT License.