1
\$\begingroup\$

This class simply allows you to use an enum while only allowing you to set a single flag. If multiple flags are set they will be rejected--unless the total of the combined flags is the same value of an existing flag. Such as Flag1 | Flag2 == Flag3 would be an example thereof.

C# 6.0:

public class RestrictedEnum<T> where T : struct, IComparable {
    /// <summary>Gets the flag set for this object.</summary>
    public T Flag { get; private set; }

    /// <summary>Constructs a RestrictedEnum<T> with the default value of T.</summary>
    public RestrictedEnum() {
        if(!typeof(T).IsEnum)
            throw new NotSupportedException();
    }

    /// <summary>Constructs a RestrictedEnum<T> with the provided flag of type T.</summary>
    /// <param name="flag">Flag to set.</param>
    public RestrictedEnum(T flag) : this() {
        SetFlag(flag);
    }

    /// <summary>Parses a member name into a member of type T if possible, otherwise null is returned.</summary>
    /// <param name="name">Name of the member to parse.</param>
    /// <returns>A nullable value of type T of the member that was parsed.</returns>
    public T? Parse(string name) {
        T value;
        return Enum.TryParse<T>(name, out value) ? (T?)value : null;
    }

    /// <summary>Attempts to set the defined flag if it exists in the Enum.</summary>
    /// <param name="flag">Enum flag to set.</param>
    /// <returns>True if the flag was set, false if the flag does not exist or the flag is already set.</returns>
    public bool SetFlag(T flag) {
        bool check = Enum.Equals(Flag, flag) && Enum.IsDefined(typeof(T), flag);
        if(check)
            Flag = flag;
        return check;
    }

    /// <summary>Gets whether the provided flag is equal to the set flag.</summary>
    /// <param name="flag">Flag to check.</param>
    /// <returns></returns>
    public bool HasFlag(T flag) =>
        Enum.Equals(Flag, flag);

    /// <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">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 obj in the sort order. Zero This instance occurs in the same position in the sort order as obj. Greater than zero This instance follows obj in the sort order.</returns>
    public int CompareTo(RestrictedEnum<T> obj) =>
        Flag.CompareTo(obj.Flag);

    /// <summary>Gets whether the the flag of an object is the same as the provided flag.</summary>
    /// <param name="obj">Object to compare the flag to.</param>
    /// <param name="flag">Flag to compare the object to.</param>
    /// <returns>True if the flag is the same as the flag of the object, else false.</returns>
    public static bool operator ==(RestrictedEnum<T> obj, T flag) =>
        obj.HasFlag(flag);

    /// <summary>Gets whether two objects contain the same flag.</summary>
    /// <param name="obj1">First object to compare.</param>
    /// <param name="obj2">Second object to compare.</param>
    /// <returns>True if the flag of each object is the same, else false.</returns>
    public static bool operator ==(RestrictedEnum<T> obj1, RestrictedEnum<T> obj2) =>
        obj1.HasFlag(obj2.Flag);

    /// <summary>Gets whether the the flag of an object is NOT the same as the provided flag.</summary>
    /// <param name="obj">Object to compare the flag to.</param>
    /// <param name="flag">Flag to compare the object to.</param>
    /// <returns>True if the flag is NOT the same as the flag of the object, else false.</returns>
    public static bool operator !=(RestrictedEnum<T> obj, T flag) =>
        !obj.HasFlag(flag);

    /// <summary>Gets whether two objects do NOT contain the same flag.</summary>
    /// <param name="obj1">First object to compare.</param>
    /// <param name="obj2">Second object to compare.</param>
    /// <returns>True if the flag of each object is NOT the same, else false.</returns>
    public static bool operator !=(RestrictedEnum<T> obj1, RestrictedEnum<T> obj2) =>
        !obj1.HasFlag(obj2.Flag);

    /// <summary>Gets the type of T of this object.</summary>
    /// <returns>The underlying type of the enum of T of this generic object.</returns>
    public Type GetUnderlyingType() =>
        typeof(T);

    /// <summary>Gets whether two objects equal (they share the same reference).</summary>
    /// <param name="obj">Object to compare.</param>
    /// <returns>True if the provided object is equal to this object.</returns>
    public bool Equals(RestrictedEnum<T> obj) =>
        this == obj;

    /// <summary>Determines whether the specified object is equal to the current object.</summary>
    /// <param name="obj">The object to compare with the current object.</param>
    /// <returns>True if the specified object is equal to the current object, else false.</returns>
    public override bool Equals(object obj) =>
        base.Equals(obj);

    /// <summary>Gest the hash code for the current object.</summary>
    /// <returns>A hash code for the current object.</returns>
    public override int GetHashCode() =>
        base.GetHashCode();
}
\$\endgroup\$
  • \$\begingroup\$ What are you looking for from the community on this? \$\endgroup\$ – Zack Feb 24 '16 at 14:20
  • \$\begingroup\$ @Zack Review on optimizations, better practices, etc. Especially in the use of operators. \$\endgroup\$ – FatalSleep Feb 24 '16 at 14:22
  • 1
    \$\begingroup\$ I'm personally not seeing anything to comment on. Your code looks good. I would do testing to make sure that your == and .equals() work as expected, especially if one of the inputs is a RestrictedEnum cast to something else. \$\endgroup\$ – Zack Feb 24 '16 at 14:46
3
\$\begingroup\$
  1. RestrictedEnum is not the best name, as it does not really tell me anything about what this class does. If it were called, say, SingleFlagEnum - that would give me an idea.
  2. T? Parse method looks out of place. I think it should either be a static method, that returns RestrictedEnum<T>, or it should be non-static void method, that modifies Flag property. I think the first option is better.
  3. check is not the best name either. What are we chekcing, exactly? Use variable name to tell us that.
  4. You might want to implement IEquatable<RestrictedEnum<T>>. And you have to make sure, that Equals(object), Equals(RestrictedEnum<T>), and == return the same result for any two given objects. At the moment they don't, and that's a huge problem and a source of future bugs. You will also have to implement GetHashCode method properly. It should return the same value for equal objects, base implementation won't do..
  5. I think you should make your class immutable if its possible. Is there any reason, why Set method has to be public?
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.