RSCG – Vogen

RSCG – Vogen
 
 

name Vogen
nuget https://www.nuget.org/packages/Vogen/
link https://dunnhq.com/posts/2021/primitive-obsession/
author Steve Dunn

Transform values( e.g. int) into classes

If you know what are ValueObject,that is one solution

 

This is how you can use Vogen .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Vogen" Version="3.0.16" />
  </ItemGroup>
<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
</Project>


The code that you will use is


// See https://aka.ms/new-console-template for more information

using DemoVogen;

Console.WriteLine("Hello, World!");
var p = PersonId.From(123);
var p1 = PersonId.From(123);
var p2 =(PersonId) 123;

Console.WriteLine(p == 123);
Console.WriteLine(p == p1);
Console.WriteLine(p == p2);



// See https://aka.ms/new-console-template for more information
using Vogen;
namespace DemoVogen;
[ValueObject<int>]
public partial struct PersonId
{
}

 

The code that is generated is

// ------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen)
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------

// Suppress warnings about [Obsolete] member usage in generated code.
#pragma warning disable CS0618

// Suppress warnings for 'Override methods on comparable types'.
#pragma warning disable CA1036

// Suppress Error MA0097 : A class that implements IComparable<T> or IComparable should override comparison operators
#pragma warning disable MA0097

// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.'
// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations.
#pragma warning disable CS8669

// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member'
#pragma warning disable CS1591

using Vogen;

namespace DemoVogen
{

    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "3.0.0.0")]
    [global::System.Text.Json.Serialization.JsonConverter(typeof(PersonIdSystemTextJsonConverter))]
[global::System.ComponentModel.TypeConverter(typeof(PersonIdTypeConverter))]

    [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(PersonIdDebugView))]
    [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: System.Int32, Value = { _value }")]
    public partial struct PersonId : global::System.IEquatable<PersonId>, global::System.IEquatable<System.Int32> ,  global::System.IComparable<PersonId>, global::System.IComparable
    {
#if DEBUG    
        private readonly global::System.Diagnostics.StackTrace _stackTrace = null;
#endif

        private readonly global::System.Boolean _isInitialized;
        
        private readonly System.Int32 _value;

        /// <summary>
        /// Gets the underlying <see cref="System.Int32" /> value if set, otherwise a <see cref="ValueObjectValidationException" /> is thrown.
        /// </summary>
        public readonly System.Int32 Value
        {
            [global::System.Diagnostics.DebuggerStepThroughAttribute]
            get
            {
                EnsureInitialized();
                return _value;
            }
        }

        [global::System.Diagnostics.DebuggerStepThroughAttribute]
        [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
        public PersonId()
        {
#if DEBUG
            _stackTrace = new global::System.Diagnostics.StackTrace();
#endif

            _isInitialized = false;
            _value = default;
        }

        [global::System.Diagnostics.DebuggerStepThroughAttribute]
        private PersonId(System.Int32 value) 
        {
            _value = value;
            _isInitialized = true;
        }

        /// <summary>
        /// Builds an instance from the provided underlying type.
        /// </summary>
        /// <param name="value">The underlying type.</param>
        /// <returns>An instance of this type.</returns>
        public static PersonId From(System.Int32 value)
        {
            

            PersonId instance = new PersonId(value);

            

            return instance;
        }

        public static explicit operator PersonId(System.Int32 value) => From(value);
        public static explicit operator System.Int32(PersonId value) => value.Value;

        // only called internally when something has been deserialized into
        // its primitive type.
        private static PersonId Deserialize(System.Int32 value)
        {
            

            

            return new PersonId(value);
        }

        public readonly global::System.Boolean Equals(PersonId other)
        {
            // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals.
            // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
            if(!_isInitialized || !other._isInitialized) return false;

            return global::System.Collections.Generic.EqualityComparer<System.Int32>.Default.Equals(Value, other.Value);
        }

        public readonly global::System.Boolean Equals(System.Int32 primitive) => Value.Equals(primitive);

        public readonly override global::System.Boolean Equals(global::System.Object obj)
        {
            return obj is PersonId && Equals((PersonId) obj);
        }

        public static global::System.Boolean operator ==(PersonId left, PersonId right) => Equals(left, right);
        public static global::System.Boolean operator !=(PersonId left, PersonId right) => !(left == right);

        public static global::System.Boolean operator ==(PersonId left, System.Int32 right) => Equals(left.Value, right);
        public static global::System.Boolean operator !=(PersonId left, System.Int32 right) => !Equals(left.Value, right);

        public static global::System.Boolean operator ==(System.Int32 left, PersonId right) => Equals(left, right.Value);
        public static global::System.Boolean operator !=(System.Int32 left, PersonId right) => !Equals(left, right.Value);

        public int CompareTo(PersonId other) => Value.CompareTo(other.Value);
        public int CompareTo(object other) {
            if(other == null) return 1;
            if(other is PersonId x) return CompareTo(x);
            throw new global::System.ArgumentException("Cannot compare to object as it is not of type PersonId", nameof(other));
        }

        
    /// <inheritdoc cref="int.TryParse(System.ReadOnlySpan{char}, System.Globalization.NumberStyles, System.IFormatProvider?, out int)"/>
    /// <summary>
    /// </summary>
    /// <returns>
    /// The value created via the <see cref="From"/> method.
    /// </returns>
    /// <exception cref="ValueObjectValidationException">Thrown when the value can be parsed, but is not valid.</exception>
    public static global::System.Boolean TryParse(global::System.ReadOnlySpan<char> s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, 
#if NETCOREAPP3_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
#endif
 out PersonId result) {
        if(System.Int32.TryParse(s, style, provider, out var r)) {
            result = From(r);
            return true;
        }

        result = default;
        return false;
    }

    /// <inheritdoc cref="int.TryParse(System.ReadOnlySpan{char}, System.IFormatProvider?, out int)"/>
    /// <summary>
    /// </summary>
    /// <returns>
    /// The value created via the <see cref="From"/> method.
    /// </returns>
    /// <exception cref="ValueObjectValidationException">Thrown when the value can be parsed, but is not valid.</exception>
    public static global::System.Boolean TryParse(global::System.ReadOnlySpan<char> s, global::System.IFormatProvider provider, 
#if NETCOREAPP3_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
#endif
 out PersonId result) {
        if(System.Int32.TryParse(s, provider, out var r)) {
            result = From(r);
            return true;
        }

        result = default;
        return false;
    }

    /// <inheritdoc cref="int.TryParse(System.ReadOnlySpan{char}, out int)"/>
    /// <summary>
    /// </summary>
    /// <returns>
    /// The value created via the <see cref="From"/> method.
    /// </returns>
    /// <exception cref="ValueObjectValidationException">Thrown when the value can be parsed, but is not valid.</exception>
    public static global::System.Boolean TryParse(global::System.ReadOnlySpan<char> s, 
#if NETCOREAPP3_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
#endif
 out PersonId result) {
        if(System.Int32.TryParse(s, out var r)) {
            result = From(r);
            return true;
        }

        result = default;
        return false;
    }

    /// <inheritdoc cref="int.TryParse(string?, System.Globalization.NumberStyles, System.IFormatProvider?, out int)"/>
    /// <summary>
    /// </summary>
    /// <returns>
    /// The value created via the <see cref="From"/> method.
    /// </returns>
    /// <exception cref="ValueObjectValidationException">Thrown when the value can be parsed, but is not valid.</exception>
    public static global::System.Boolean TryParse(string s, global::System.Globalization.NumberStyles style, global::System.IFormatProvider provider, 
#if NETCOREAPP3_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
#endif
 out PersonId result) {
        if(System.Int32.TryParse(s, style, provider, out var r)) {
            result = From(r);
            return true;
        }

        result = default;
        return false;
    }

    /// <inheritdoc cref="int.TryParse(string?, System.IFormatProvider?, out int)"/>
    /// <summary>
    /// </summary>
    /// <returns>
    /// The value created via the <see cref="From"/> method.
    /// </returns>
    /// <exception cref="ValueObjectValidationException">Thrown when the value can be parsed, but is not valid.</exception>
    public static global::System.Boolean TryParse(string s, global::System.IFormatProvider provider, 
#if NETCOREAPP3_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
#endif
 out PersonId result) {
        if(System.Int32.TryParse(s, provider, out var r)) {
            result = From(r);
            return true;
        }

        result = default;
        return false;
    }

    /// <inheritdoc cref="int.TryParse(string?, out int)"/>
    /// <summary>
    /// </summary>
    /// <returns>
    /// The value created via the <see cref="From"/> method.
    /// </returns>
    /// <exception cref="ValueObjectValidationException">Thrown when the value can be parsed, but is not valid.</exception>
    public static global::System.Boolean TryParse(string s, 
#if NETCOREAPP3_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)]
#endif
 out PersonId result) {
        if(System.Int32.TryParse(s, out var r)) {
            result = From(r);
            return true;
        }

        result = default;
        return false;
    }


        public readonly override global::System.Int32 GetHashCode() => global::System.Collections.Generic.EqualityComparer<System.Int32>.Default.GetHashCode(_value);

        /// <summary>Returns the string representation of the underlying type</summary>
    /// <inheritdoc cref="System.Int32.ToString()" />
    public readonly override global::System.String ToString() =>_isInitialized ? Value.ToString() : "[UNINITIALIZED]";

        private readonly void EnsureInitialized()
        {
            if (!_isInitialized)
            {
#if DEBUG
                global::System.String message = "Use of uninitialized Value Object at: " + _stackTrace ?? "";
#else
                global::System.String message = "Use of uninitialized Value Object.";
#endif

                throw new global::Vogen.ValueObjectValidationException(message);
            }
        }

        
 
        
        class PersonIdSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<PersonId>
        {
            public override PersonId Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
            {
                return PersonId.Deserialize(reader.GetInt32());
            }

            public override void Write(System.Text.Json.Utf8JsonWriter writer, PersonId value, global::System.Text.Json.JsonSerializerOptions options)
            {
                writer.WriteNumberValue(value.Value);
            }

#if NET6_0_OR_GREATER            
            public override PersonId ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
            {
                return PersonId.Deserialize(global::System.Int32.Parse(reader.GetString(), global::System.Globalization.NumberStyles.Any, global::System.Globalization.CultureInfo.InvariantCulture));
            }

            public override void WriteAsPropertyName(System.Text.Json.Utf8JsonWriter writer, PersonId value, global::System.Text.Json.JsonSerializerOptions options)
            {
                writer.WritePropertyName(value.Value.ToString(global::System.Globalization.CultureInfo.InvariantCulture));
            }
#endif
        }


        class PersonIdTypeConverter : global::System.ComponentModel.TypeConverter
        {
            public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
            {
                return sourceType == typeof(global::System.Int32) || sourceType == typeof(global::System.String) || base.CanConvertFrom(context, sourceType);
            }

            public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value)
            {
                return value switch
                {
                    global::System.Int32 intValue => PersonId.Deserialize(intValue),
                    global::System.String stringValue when !global::System.String.IsNullOrEmpty(stringValue) && global::System.Int32.TryParse(stringValue, out var result) => PersonId.Deserialize(result),
                    _ => base.ConvertFrom(context, culture, value),
                };
            }

            public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType)
            {
                return sourceType == typeof(global::System.Int32) || sourceType == typeof(global::System.String) || base.CanConvertTo(context, sourceType);
            }

            public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType)
            {
                if (value is PersonId idValue)
                {
                    if (destinationType == typeof(global::System.Int32))
                    {
                        return idValue.Value;
                    }

                    if (destinationType == typeof(global::System.String))
                    {
                        return idValue.Value.ToString();
                    }
                }

                return base.ConvertTo(context, culture, value, destinationType);
            }
        }





        internal sealed class PersonIdDebugView
        {
            private readonly PersonId _t;

            PersonIdDebugView(PersonId t)
            {
                _t = t;
            }

            public global::System.Boolean IsInitialized => _t._isInitialized;
            public global::System.String UnderlyingType => "System.Int32";
            public global::System.String Value => _t._isInitialized ? _t._value.ToString() : "[not initialized]" ;

            #if DEBUG
            public global::System.String CreatedWith => _t._stackTrace?.ToString() ?? "the From method";
            #endif

            public global::System.String Conversions => @"Default";
                }

}
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/Vogen