标志的自定义枚举基类



我正在按照微软的建议制作一个自定义枚举类,但很难制作出一个支持标志样式枚举的版本。

当尝试按位或将两个实例组合在一起以创建一个不存在的新实例时,就会出现此问题。

public abstract class Enumeration<TEnum>
where TEnum : Enumeration<TEnum>
{
public int Id { get; private set; }
public string Name { get; private set; }
protected Enumeration(int id, string name) => (Id, Name) = (id, name);
public override string ToString() => Name;
public static IEnumerable<TEnum> GetAll() =>
typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
.Where(info => enumerationType.IsAssignableFrom(info.FieldType))
.Select(f => f.GetValue(null))
.Cast<T>();
public static Enumeration<TEnum> operator |(Enumeration<TEnum> left, Enumeration<TEnum> right)
{
// This is the problem method!
// I can obviously bitwise or together the two values, but how do I create an instance
// of TEnum from here so that is has a valid name?
// For instance, Colors.Red | Colors.Blue would need to be an instance where
// Id == (1 << 0 | 1 << 1) and Name == "Red, Blue".
}
// Other utility methods ...
}
public class Colors : Enumeration<Colors>
{
public static readonly Colors Red = new Colors(1 << 0, "Red");
public static readonly Colors Blue = new Colors(1 << 1, "Blue");
public static readonly Colors Green = new Colors(1 << 2, "Green");
public Colors(int id, string name) : base(id, name) { }
}

如果这是一个xy问题,我也很乐意听取其他想法。

问题始终是无法从抽象基类内部构造Colors的实例。即,您可以将泛型约束为具有new(),但不能像new(int, string)那样具有特定的构造函数。

因此,一种选择是在具体类本身中定义(并为枚举的每个实例重新定义(运算符

public class Colors : Enumeration<Colors>
{
public static readonly Colors Red = new Colors(1 << 0, "Red");
public static readonly Colors Blue = new Colors(1 << 1, "Blue");
public static readonly Colors Green = new Colors(1 << 2, "Green");
public Colors(int id, string name) : base(id, name) { }

public static Colors operator |(Colors left, Colors right)
{
return new Colors(left.Id | right.Id, $"{left.Name}, {right.Name}");
}
}

这是一个活生生的例子,但我怀疑这不会是你想做的:https://dotnetfiddle.net/KBQEt4

另一种选择是赋予每个枚举创建自己的智能(即,绕过缺乏特定构造函数约束的问题(,然后可以在基类上使用运算符:

public abstract class Enumeration<TEnum>
where TEnum : Enumeration<TEnum>
{
public int Id { get; private set; }
public string Name { get; private set; }

protected Enumeration(int id, string name) => (Id, Name) = (id, name);
public override string ToString() => Name;
public static Enumeration<TEnum> operator |(Enumeration<TEnum> left, Enumeration<TEnum> right)
{
return left.Create(left.Id | right.Id, $"{left.Name}, {right.Name}");
}

protected abstract Enumeration<TEnum> Create(int id, string name);

// Other utility methods ...
}
public class Colors : Enumeration<Colors>
{
public static readonly Colors Red = new Colors(1 << 0, "Red");
public static readonly Colors Blue = new Colors(1 << 1, "Blue");
public static readonly Colors Green = new Colors(1 << 2, "Green");
public Colors(int id, string name) : base(id, name) { }

protected override Enumeration<Colors> Create(int id, string name) => new Colors(id,name);

}

现场示例:https://dotnetfiddle.net/8ZBQqk

最新更新