cómo usar el atributo flags con la clase heredada de la enumeración

cómo usar el atributo flags con la clase heredada de la enumeración

Estás complicando demasiado esto. El primer problema que sospecho es que usted (o su analista comercial) no tiene suficiente comprensión del tema comercial, es decir, turno . Lo que tienes aquí son dos enumeraciones diferentes:

public enum ScheduleType 
{
    Unknown = 0,
    Fixed,
    Rotated
}

public enum ScheduleLoad 
{
    Unknown = 0,
    FullTime,
    PartTime
}

Luego, en la interfaz de usuario, necesita dos cuadros desplegables / grupos de radio diferentes para permitir que el usuario organice el diseño del turno, luego guarde esto en dos propiedades diferentes de su objeto.

Sin embargo, si insiste en tener esto en una enumeración, por lo tanto, una propiedad con valores de enumeración marcados, debe validar la entrada del usuario antes de guardar las banderas en su tienda.

[Flags]
public enum ShiftLayout
{
    Unknown = 0,
    Fixed = 1,
    Rotated = 2,
    FullTime = 4,
    PartTime = 8,
    Flexible = 16
}

Entonces la validación se realiza así:

public bool IsShiftLayoutValid(ShiftLayout layout)
{
    var isValid = layout.HasFlag(ShiftLayout.Flexible) 
        && (layout & ~ShiftLayout.Flexible) == ShiftLayout.Unknown;

    if (!isValid && !layout.HasFlag(ShiftLayout.Flexible))
    {
        var hasValidSchedule = (layout.HasFlag(ShiftLayout.Fixed) && !layout.HasFlag(ShiftLayout.Rotated))
            || layout.HasFlag(ShiftLayout.Rotated);

        var hasValidTime = (layout.HasFlag(ShiftLayout.FullTime) && !layout.HasFlag(ShiftLayout.PartTime))
            || layout.HasFlag(ShiftLayout.PartTime);

        isValid = hasValidSchedule && hasValidTime;
    }

    return isValid;
}

Este siguiente ScheduleType El ejemplo tiene la capacidad de contener múltiples tipos de forma similar a cómo se usan los campos de bits. Tenga en cuenta los valores hexadecimales utilizados para el valor de los tipos que permitirían operaciones lógicas para determinar qué tipos componen el valor actual.

public class ScheduleType : FlagsValueObject<ScheduleType> {
    public static readonly ScheduleType Fixed = new ScheduleType(0x01, "Fixed");
    public static readonly ScheduleType Flexible = new ScheduleType(0x02, "Flexible");
    public static readonly ScheduleType FullTime = new ScheduleType(0x04, "Full Time");
    public static readonly ScheduleType PartTime = new ScheduleType(0x08, "Part Time");
    public static readonly ScheduleType Rotated = new ScheduleType(0x10, "Rotated");

    protected ScheduleType(int value, string name)
        : base(value, name) {
    }

    private ScheduleType(ScheduleType a, ScheduleType b) {
        foreach (var kvp in a.Types.Union(b.Types)) {
            Types[kvp.Key] = kvp.Value;
        }            
        Name = string.Join(", ", Types.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Value)) + " Work Schedule";
        Value = Types.Keys.Sum();
    }

    protected override ScheduleType Or(ScheduleType other) {
        var result = new ScheduleType(this, other);

        //Applying validation rules on new combination
        if (result.HasFlag(Fixed) && result.HasFlag(Rotated))
            throw new InvalidOperationException("ScheduleType cannot be both Fixed and Rotated");

        if (result.HasFlag(FullTime) && result.HasFlag(PartTime))
            throw new InvalidOperationException("ScheduleType cannot be both FullTime and PartTime");

        return result;
    }
}

Usando el HasFlag para determinar qué combinación existe dentro de la bandera, se pueden aplicar las reglas comerciales deseadas.

por ejemplo

//Applying validation rules on new combination
if (result.HasFlag(Fixed) && result.HasFlag(Rotated))
    throw new InvalidOperationException("ScheduleType cannot be both Fixed and Rotated");

if (result.HasFlag(FullTime) && result.HasFlag(PartTime))
    throw new InvalidOperationException("ScheduleType cannot be both FullTime and PartTime");

Las reglas se aplicaron al combinar banderas para evitar la creación de combinaciones no deseadas.

Se deriva de los siguientes objetos de valor de apoyo

BanderasValorObjeto

public abstract class FlagsValueObject<T> : EnumValueObject where T : FlagsValueObject<T> {
    protected readonly IDictionary<int, string> Types = new SortedDictionary<int, string>();

    protected FlagsValueObject(int value, string name)
        : base(value, name) {
        Types[value] = name;
    }

    protected FlagsValueObject() {

    }

    public static T operator |(FlagsValueObject<T> left, T right) {
        return left.Or(right);
    }

    protected abstract T Or(T other);

    public virtual bool HasFlag(T flag) {
        return flag != null && (Value & flag.Value) == flag.Value;
    }

    public virtual bool HasFlagValue(int value) {
        return (Value & value) == value;
    }
}

EnumValueObject

public class EnumValueObject : IEquatable<EnumValueObject>, IComparable<EnumValueObject> {

    protected EnumValueObject(int value, string name) {
        Value = value;
        Name = name;
    }

    protected EnumValueObject() {

    }

    public virtual string Name { get; protected set; }

    public virtual int Value { get; protected set; }

    public static bool operator ==(EnumValueObject left, EnumValueObject right) {
        return Equals(left, right);
    }

    public static bool operator !=(EnumValueObject left, EnumValueObject right) {
        return !Equals(left, right);
    }

    public int CompareTo(EnumValueObject other) {
        return Value.CompareTo(other.Value);
    }

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

    public override bool Equals(object obj) {
        return obj is EnumValueObject && Equals((EnumValueObject)obj);
    }

    public override int GetHashCode() {
        return Value.GetHashCode();
    }

    public override string ToString() {
        return Name;
    }
}

Prueba unitaria de ejemplo simple.

[TestClass]
public class ScheduleTypeValueObjectTests {
    [TestMethod]
    public void Should_Merge_Names() {
        //Arrange
        var fixedSchedult = ScheduleType.Fixed; //Fixed Work Schedule
        var fullTime = ScheduleType.FullTime; // Full Time Work Schedule
        var type = fixedSchedult | fullTime;

        //Act
        var actual = type.Name;

        //Assert
        actual.Should().Be("Fixed, Full Time Work Schedule");
    }


    [TestMethod]
    [ExpectedException(typeof(InvalidOperationException))]
    public void Should_Fail_Bitwise_Combination() {
        //Arrange
        var fullTime = ScheduleType.FullTime; // Full Time Work Schedule
        var partTime = ScheduleType.PartTime;

        var value = fullTime | partTime;
    }
}

El HasFlag La propiedad permite verificar qué tipos existen dentro de la bandera, como se demuestra en el siguiente ejemplo.

public class Schedule {
    public Schedule(
        //...
        ScheduleType scheduleType
        //...
        ) {

        //...

        ScheduleType = scheduleType;
    }

    //...

    public ScheduleType ScheduleType { get; set; }
    public bool IsFixed {
        get {
            return ScheduleType != null && ScheduleType.HasFlag(ScheduleType.Fixed);
        }
    }
    public bool IsFlexible {
        get {
            return
                ScheduleType != null && ScheduleType.HasFlag(ScheduleType.Flexible);
        }
    }
    public bool IsFullTime {
        get {
            return
                ScheduleType != null && ScheduleType.HasFlag(ScheduleType.FullTime);
        }
    }

    //...
}

Solo usa 2 enumeraciones.

1 por tipo de trabajo (fijo, etc.) y 1 por carga de trabajo (tiempo completo, etc.)

luego un bool para flexible.

No compliques las cosas sin razón, ya que, al ver lo que hiciste, pusiste mucho código innecesario para hacer una comparación.

Si realmente desea mantener todo en una enumeración, ahorrará mucho más código haciendo una enumeración como

  • Corregido
  • Tiempo completo fijo
  • Tiempo parcial fijo
  • Rotado
  • Tiempo completo rotado
  • Tiempo parcial rotado

etc etc etc con todas las combinaciones.

Tiene una cantidad baja de combinaciones de enumeración y no vale la pena hacer un código personalizado para verificar todas las combinaciones con un IComparable

Simplemente use enumeraciones diferentes y en su clase de horario esos

public bool IsFixed { get; }
public bool IsFlexible { get; }
public bool IsFullTime { get; }

con una comparación entre Fijo/Rotado, Tiempo completo/Tiempo parcial, etc.

o use solo una sola enumeración.