EXILED icon indicating copy to clipboard operation
EXILED copied to clipboard

Review

Open warquys opened this issue 6 months ago • 6 comments

I don't want to impose my decisions. But, I want this to be change or at least be heard. Some stuffs seem to me to have been rushed. So i added comamnt and do litle edition. I don't know how you prefer to resolve the problem. So i pointed them with comment for the most.

Why not discord: Discussions here will be easier than discord to keep up with change.

I also try to use the new Enum. But realy i do not get who it work.

When I fold the code, it seems to me that there is an error in the management of super classes. And when i try, This isn't working, I don't know if it's me or the code. So I place my experiences here:

Interactive Test
> #reset core
Resetting execution engine.
Loading context from 'CSharpInteractive.rsp'.
> using System.Reflection;
>    /// <summary>
     /// An interface for all enum classes.
     /// </summary>
     public interface IEnumClass
     {

     }
> public abstract class UniqueUnmanagedEnumClass<TSource, TObject> : IComparable, IEquatable<TObject>, IComparable<TObject>, IComparer<TObject>, IConvertible, IEnumClass
        where TSource : unmanaged, IComparable, IFormattable, IConvertible, IComparable<TSource>, IEquatable<TSource>
        where TObject : UniqueUnmanagedEnumClass<TSource, TObject>
    {
        private static SortedList<TSource, TObject> values;
        private static int nextValue = int.MinValue;
        private static bool isDefined;

        private string name;

        /// <summary>
        /// Initializes a new instance of the <see cref="UniqueUnmanagedEnumClass{TSource, TObject}"/> class.
        /// </summary>
        public UniqueUnmanagedEnumClass()
        {
            values ??= new();
            TypeCode code = Convert.GetTypeCode(typeof(TSource).GetField("MinValue").GetValue(null));

            // @Nao If the value is not an Uxxxxx it will get an overflowLike if it use an Int16, Byte or SByte
            // Maybe use a long for nextValue and do "nextValue = typeof(TSource).GetField("MinValue").GetValue(null)"
            // it also be a struct containing only unmanged structThe best solution is proably to
            // look for upper version of the C# to resolve this with the new numeric interfaces.
            if (code is TypeCode.UInt16 or TypeCode.UInt32 or TypeCode.UInt64)
                nextValue = 0;

            lock (values)
            {
                TSource value;
                do
                {
                    value = (TSource)Convert.ChangeType(nextValue++, code);
                }
                while (values.ContainsKey(value));

                Value = value;

                // @nao If the value is register when a new instance is created.
                // Maybe put the ctor in protected to avoid exteranal code (outisde the enum) to create new values
                values.Add(value, (TObject)this);
            }
        }

        /// <summary>
        /// Gets all <typeparamref name="TObject"/> object instances.
        /// </summary>
        public static IEnumerable<TObject> Values => values.Values;

        /// <summary>
        /// Gets the value of the enum item.
        /// </summary>
        public TSource Value { get; }

        /// <summary>
        /// Gets the name determined from reflection.
        /// </summary>
        public string Name
        {
            get
            {
                if (isDefined)
                    return name;

                IEnumerable<FieldInfo> fields = typeof(TObject)
                    .GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)
                    .Where(t => t.FieldType == typeof(TObject));

                foreach (FieldInfo field in fields)
                {
                    TObject instance = (TObject)field.GetValue(null);
                    instance.name = field.Name;
                }

                isDefined = true;
                return name;
            }
        }

        /// <summary>
        /// Implicitly converts the <see cref="UniqueUnmanagedEnumClass{TSource, TObject}"/> to <typeparamref name="TSource"/>.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        public static implicit operator TSource(UniqueUnmanagedEnumClass<TSource, TObject> value) => value.Value;

        /// <summary>
        /// Implicitly converts the <typeparamref name="TSource"/> to <see cref="EnumClass{TSource, TObject}"/>.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        public static implicit operator UniqueUnmanagedEnumClass<TSource, TObject>(TSource value) => values[value];

        /// <summary>
        /// Implicitly converts the <see cref="UniqueUnmanagedEnumClass{TSource, TObject}"/> to <typeparamref name="TObject"/>.
        /// </summary>
        /// <param name="value">The value to convert.</param>
        public static implicit operator TObject(UniqueUnmanagedEnumClass<TSource, TObject> value) => values[value];

        /// <summary>
        /// Casts the specified <paramref name="value"/> to the corresponding type.
        /// </summary>
        /// <param name="value">The enum value to be cast.</param>
        /// <returns>The cast object.</returns>
        public static TObject Cast(TSource value) => values[value];

        /// <summary>
        /// Casts the specified <paramref name="values"/> to the corresponding type.
        /// </summary>
        /// <param name="values">The enum values to be cast.</param>
        /// <returns>The cast object.</returns>
        public static IEnumerable<TObject> Cast(IEnumerable<TSource> values)
        {
            foreach (TSource value in values)
                yield return UniqueUnmanagedEnumClass<TSource, TObject>.values[value];
        }

        /// <summary>
        /// Casts the specified <paramref name="values"/> to the corresponding type.
        /// </summary>
        /// <typeparam name="T">The type to cast the enum to.</typeparam>
        /// <param name="values">The enum values to be cast.</param>
        /// <returns>The cast <typeparamref name="T"/> object.</returns>
        public static IEnumerable<T> Cast<T>(IEnumerable<TSource> values)
            where T : TObject
        {
            foreach (TSource value in values)
                yield return Cast<T>(value);
        }

        /// <summary>
        /// Casts the specified <paramref name="value"/> to the corresponding type.
        /// </summary>
        /// <typeparam name="T">The type to cast the enum to.</typeparam>
        /// <param name="value">The enum value to be cast.</param>
        /// <returns>The cast <typeparamref name="T"/> object.</returns>
        public static T Cast<T>(TSource value)
            where T : TObject => (T)values[value];

        /// <summary>
        /// Safely casts the specified <paramref name="value"/> to the corresponding type.
        /// </summary>
        /// <param name="value">The enum value to be cast.</param>
        /// <param name="result">The cast <paramref name="value"/>.</param>
        /// <returns><see langword="true"/> if the <paramref name="value"/> was cast; otherwise, <see langword="false"/>.</returns>
        public static bool SafeCast(TSource value, out TObject result) => values.TryGetValue(value, out result);

        /// <summary>
        /// Safely casts the specified <paramref name="values"/> to the corresponding type.
        /// </summary>
        /// <param name="values">The enum value to be cast.</param>
        /// <param name="results">The cast <paramref name="values"/>.</param>
        /// <returns><see langword="true"/> if the <paramref name="values"/> was cast; otherwise, <see langword="false"/>.</returns>
        public static bool SafeCast(IEnumerable<TSource> values, out IEnumerable<TObject> results)
        {
            results = null;

            List<TObject> tmpValues = new List<TObject>();
            foreach (TSource value in values)
            {
                if (!UniqueUnmanagedEnumClass<TSource, TObject>.values.TryGetValue(value, out TObject result))
                    return false;

                tmpValues.Add(result);
            }

            results = tmpValues;
            return true;
        }

        /// <summary>
        /// Retrieves an array of the values of the constants in a specified <see cref="UnmanagedEnumClass{TSource, TObject}"/>.
        /// </summary>
        /// <param name="type">The <see cref="UnmanagedEnumClass{TSource, TObject}"/> type.</param>
        /// <returns>An array of the values of the constants in a specified <see cref="UnmanagedEnumClass{TSource, TObject}"/>.</returns>
        public static TSource[] GetValues(Type type)
        {
            if (type is null)
                throw new NullReferenceException("The specified type parameter is null");

            if (!type.IsSubclassOf(typeof(UniqueUnmanagedEnumClass<TSource, TObject>)) && type.BaseType != typeof(UniqueUnmanagedEnumClass<TSource, TObject>))
                throw new Exception("The specified type parameter is not a UniqueUnmanagedEnumClass<TSource, TObject> type.");

            return typeof(TSource)
                .GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.GetField)
                .Where(field => field.FieldType == typeof(TSource))
                .Select(field => (TSource)field.GetValue(null))
                .ToArray();
        }

        /// <summary>
        /// Parses a <see cref="string"/> object.
        /// </summary>
        /// <param name="obj">The object to be parsed.</param>
        /// <returns>The corresponding <typeparamref name="TObject"/> object instance, or <see langword="null"/> if not found.</returns>
        public static TObject Parse(string obj)
        {
            foreach (TObject value in values.Values.Where(value => string.Compare(value.Name, obj, StringComparison.OrdinalIgnoreCase) == 0))
                return value;

            return null;
        }

        /// <summary>
        /// Converts the <see cref="UnmanagedEnumClass{TSource, TObject}"/> instance to a human-readable <see cref="string"/> representation.
        /// </summary>
        /// <returns>A human-readable <see cref="string"/> representation of the <see cref="UnmanagedEnumClass{TSource, TObject}"/> instance.</returns>
        public override string ToString() => Name;

        /// <summary>
        /// Determines whether the specified object is equal to the current object.
        /// </summary>
        /// <param name="obj">The object to compare.</param>
        /// <returns><see langword="true"/> if the object was equal; otherwise, <see langword="false"/>.</returns>
        public override bool Equals(object obj) =>
            obj != null && (obj is TSource value ? Value.Equals(value) : obj is TObject derived && Value.Equals(derived.Value));

        /// <summary>
        /// Determines whether the specified object is equal to the current object.
        /// </summary>
        /// <param name="other">The object to compare.</param>
        /// <returns><see langword="true"/> if the object was equal; otherwise, <see langword="false"/>.</returns>
        public bool Equals(TObject other) => Value.Equals(other.Value);

        /// <summary>
        /// Returns a the 32-bit signed hash code of the current object instance.
        /// </summary>
        /// <returns>The 32-bit signed hash code of the current object instance.</returns>
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Compares the current instance with another object of the same type and returns
        /// an integer that indicates whether the current instance precedes, follows, or
        /// occurs in the same position in the sort order as the other object.
        /// </summary>
        /// <param name="other">An object to compare with this instance.</param>
        /// <returns>
        /// A value that indicates the relative order of the objects being compared.
        /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order.
        /// Zero This instance occurs in the same position in the sort order as other.
        /// Greater than zero This instance follows other in the sort order.
        /// </returns>
        public int CompareTo(TObject other) => Value.CompareTo(other.Value);

        /// <summary>
        /// Compares the current instance with another object of the same type and returns
        /// an integer that indicates whether the current instance precedes, follows, or
        /// occurs in the same position in the sort order as the other object.
        /// </summary>
        /// <param name="obj">An object to compare with this instance.</param>
        /// <returns>
        /// A value that indicates the relative order of the objects being compared.
        /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order.
        /// Zero This instance occurs in the same position in the sort order as other.
        /// Greater than zero This instance follows other in the sort order.
        /// </returns>
        public int CompareTo(object obj) =>
            obj == null ? -1 : obj is TSource value ? Value.CompareTo(value) : obj is TObject derived ? Value.CompareTo(derived.Value) : -1;

        /// <summary>
        /// Compares the specified object instance with another object of the same type and returns
        /// an integer that indicates whether the current instance precedes, follows, or
        /// occurs in the same position in the sort order as the other object.
        /// </summary>
        /// <param name="x">An object to compare.</param>
        /// <param name="y">Another object to compare.</param>
        /// <returns>
        /// A value that indicates the relative order of the objects being compared.
        /// The return value has these meanings: Value Meaning Less than zero This instance precedes other in the sort order.
        /// Zero This instance occurs in the same position in the sort order as other.
        /// Greater than zero This instance follows other in the sort order.
        /// </returns>
        public int Compare(TObject x, TObject y) => x == null ? -1 : y == null ? 1 : x.Value.CompareTo(y.Value);

		/// <inheritdoc/>
        TypeCode IConvertible.GetTypeCode() => Value.GetTypeCode();

        /// <inheritdoc/>
        bool IConvertible.ToBoolean(IFormatProvider provider) => Value.ToBoolean(provider);

        /// <inheritdoc/>
        char IConvertible.ToChar(IFormatProvider provider) => Value.ToChar(provider);

        /// <inheritdoc/>
        sbyte IConvertible.ToSByte(IFormatProvider provider) => Value.ToSByte(provider);

        /// <inheritdoc/>
        byte IConvertible.ToByte(IFormatProvider provider) => Value.ToByte(provider);

        /// <inheritdoc/>
        short IConvertible.ToInt16(IFormatProvider provider) => Value.ToInt16(provider);

        /// <inheritdoc/>
        ushort IConvertible.ToUInt16(IFormatProvider provider) => Value.ToUInt16(provider);

        /// <inheritdoc/>
        int IConvertible.ToInt32(IFormatProvider provider) => Value.ToInt32(provider);

        /// <inheritdoc/>
        uint IConvertible.ToUInt32(IFormatProvider provider) => Value.ToUInt32(provider);

        /// <inheritdoc/>
        long IConvertible.ToInt64(IFormatProvider provider) => Value.ToInt64(provider);

        /// <inheritdoc/>
        ulong IConvertible.ToUInt64(IFormatProvider provider) => Value.ToUInt64(provider);

        /// <inheritdoc/>
        float IConvertible.ToSingle(IFormatProvider provider) => Value.ToSingle(provider);

        /// <inheritdoc/>
        double IConvertible.ToDouble(IFormatProvider provider) => Value.ToDouble(provider);

        /// <inheritdoc/>
        decimal IConvertible.ToDecimal(IFormatProvider provider) => Value.ToDecimal(provider);

        /// <inheritdoc/>
        DateTime IConvertible.ToDateTime(IFormatProvider provider) => Value.ToDateTime(provider);

        /// <inheritdoc/>
        string IConvertible.ToString(IFormatProvider provider) => ToString();

        /// <inheritdoc/>
        object IConvertible.ToType(Type conversionType, IFormatProvider provider) => Value.ToType(conversionType, provider);
    }

> public class Enum1 : UniqueUnmanagedEnumClass<uint, Enum1>
{
    /// <summary>
    /// Represents an invalid custom role.
    /// </summary>
    public static readonly Enum1 None = new();

    public static readonly Enum1 Foo = new();

    public static readonly Enum1 Bar = new();
}

public class Enum2 : UniqueUnmanagedEnumClass<uint, Enum1>
{
    /// <summary>
    /// Represents an invalid custom role.
    /// </summary>
    public static readonly Enum1 None = new();

    public static readonly Enum1 Foo = new();
    
        public static readonly Enum1 Bar = new();
}

public class Enum1Ex : Enum1
{
    public static readonly Enum1 ExtenedValue = new();

    public static readonly Enum1Ex ExtenedValue2 = new();
}

public class Enum2Ex : Enum2
{
    public static readonly Enum2 ExtenedValue1 = new();

    public static readonly Enum2Ex ExtenedValue2 = new();
}
> Enum1.Bar.Name
"Bar"
> Enum1Ex.Bar.Name
"Bar"
> Enum1Ex.ExtenedValue.Name
null
> Enum1Ex.ExtenedValue2.Name
null
> Enum2Ex.ExtenedValue1.Name
System.TypeInitializationException: The type initializer for 'Enum2Ex' threw an exception.
> Enum2Ex.ExtenedValue2.Name
System.TypeInitializationException: The type initializer for 'Enum2Ex' threw an exception.
> Enum2Ex.ExtenedValue2
System.TypeInitializationException: The type initializer for 'Enum2Ex' threw an exception.
> try { _ = Enum2Ex.ExtenedValue2.Name; } catch (Exception e) { WriteLine(e?.InnerException?.ToString() ?? "None"); }
System.InvalidCastException: Unable to cast object of type 'Enum2' to type 'Enum1'.
   at Submission#4.UniqueUnmanagedEnumClass`2..ctor()
   at Submission#5.Enum2..ctor()
   at Submission#5.Enum2Ex..cctor()
> public class Enum1 : UniqueUnmanagedEnumClass<uint, Enum1>
{
    /// <summary>
    /// Represents an invalid custom role.
    /// </summary>
    public static readonly Enum1 None = new();

    public static readonly Enum1 Foo = new();

    public static readonly Enum1 Bar = new();
}

public class Enum2 : UniqueUnmanagedEnumClass<uint, Enum2>
{
    /// <summary>
    /// Represents an invalid custom role.
    /// </summary>
    public static readonly Enum1 None = new();

    public static readonly Enum1 Foo = new();
    
    public static readonly Enum1 Bar = new();
}

public class Enum1Ex : Enum1
{
    public static readonly Enum1 ExtenedValue1 = new();

    public static readonly Enum1Ex ExtenedValue2 = new();
}

public class Enum2Ex : Enum2
{
    public static readonly Enum2 ExtenedValue1 = new();

    public static readonly Enum2Ex ExtenedValue2 = new();
}
> Enum1.Bar.Name
"Bar"
> Enum1Ex.Bar.Name
"Bar"
> Enum1Ex.ExtenedValue1.Name
null
> Enum1Ex.ExtenedValue2.Name
null
> Enum2Ex.ExtenedValue1.Name
null

warquys avatar Aug 07 '24 22:08 warquys