简体   繁体   English

强制一个类实现没有值约束的枚举

[英]Force a class to implement an enum with no value constrain

I've different classes (eg: Player, Enemy, Asteroids...) - that are supposed to have one of different statuses (eg: ok, teleporting...). 我有不同的类别(例如:玩家,敌人,小行星...)-应该具有不同的状态之一(例如:确定,传送...)。

enum Status
{
    Ok,
    Teleporting,
    ...
};

Using an interface , I'd like to force those classes to declare only the existance of a enum status variable inside them. 使用接口 ,我想强制这些类声明它们内部是否存在枚举状态变量。 However, I want no constraints on values . 但是,我不希望值受到任何限制 That is, a Player and an Enemy can have different values inside status (eg: one can teleport, the other one can't). 也就是说,玩家和敌人的内部状态值可以不同(例如:一个可以传送,另一个可以不传送)。

How can I do that? 我怎样才能做到这一点?

Update1 UPDATE1

public enum Status
{
    Ok,
    Teleporting
};

public interface IHaveStatus
{
    Status Status { get; set; }
}

Update 2 更新2

public enum Status
{
    Ok,
    Teleporting
};

public interface IHaveStatus
{
    Status status;
}

You could declare the interface as follows: 您可以如下声明接口:

public interface IHaveStatus {
    Status Status { get; set; }
}

and let the specific classes implement that interface: 并让特定的类实现该接口:

class Player: IHaveStatus {
    public Status Status { get; set; }
}

This will not limit the usage of the status field, it can have any value that is legal for the Status enum. 这不会限制status字段的使用,它可以具有对Status枚举合法的任何值。

If (however) you would like to apply some logic (enemy can't teleport), than this may not be the right design. 如果(但是)您想应用一些逻辑(敌人无法传送),那么这可能不是正确的设计。

If you want to specify that players have certain capabillities (ie: teleporting) you could use an attribute or an interface to identify those classes. 如果要指定播放器具有某些功能(例如,传送),则可以使用属性或接口来标识那些类别。

Using interfaces: 使用接口:

public interface ICanTeleport { }

public class Player : ICanTeleport { ... }

you can determine capabillities using a cast: 您可以使用演员表确定能力:

if(somePlayer is ICanTeleport) { .. }

or when using attributes : 或使用属性时

[System.AttributeUsage(System.AttributeTargets.Class)]  
public class Teleporter : System.Attribute  
{ }

[Teleporter()]
public class Player { ... }

Then you will have to use reflection to determine the attributes, see this question . 然后,您将必须使用反射来确定属性,请参阅此问题

  • Using interfaces makes the behavior bound to the class inheritance. 使用接口使行为绑定到类继承。 It also gives you the abillity to add methods to the interface (ie a Teleport(coordinates) method). 它还使您有能力向接口添加方法(即Teleport(coordinates)方法)。

  • Using attributes gives more possibillites to mix and match capabillities on the fly, as there is no class hierarchy forcing the capabillities. 使用属性可以使更多的可能的对象即时混合和匹配功能,因为没有类别层次会强制这些功能。

Unless I'm missing something obvious, a simple interface like this will do it: 除非我遗漏了一些明显的东西,否则像这样的简单界面就可以做到:

 public interface IHaveStatus
 {
      Status Status {get;}
 }

Values constraints I would put in the concrete classes. 我将在具体类中放入值约束。 For instance, if a player can't teleport, a good place to constraint that would be in the property setter: 例如,如果玩家无法传送,则在属性设置器中放置约束的好地方:

public class Player : IHaveStatus
{
    private Status _status;

    public Status Status 
    {
        get {return _status;}
        set
        {
            // a player can't teleport...
            if(value != Status.Teleport)
            {
                _status = value;
            }
        }
    }
}

You can't, at least not with the same enum type. 您不能,至少不能使用相同的枚举类型。 You would have to declare different enums for every set of allowed values. 您将必须为每组允许的值声明不同的枚举。 Pro: you get compile time checking (as long as you explicitly assign values as is usually the case with Enums values). Pro:您可以进行编译时检查(只要您像Enums值那样通常显式地分配值)。 Con: You have to declare a new Enum for every allowable range. 缺点:您必须为每个允许的范围声明一个新的Enum。

Alternatively you check the incoming value at run time at the type level in the setter (method or property) and enforce any constraint there. 或者,您可以在运行时在setter (方法或属性)的类型级别上检查传入的值,并在其中强制执行任何约束。 Pro: you only need one enum for the complete range. 优点:整个范围只需要一个枚举。 Con: The downside of this solution is you could have bugs that you will not catch until runtime. 缺点:此解决方案的缺点是您可能会遇到一些错误,直到运行时才能发现它们。

Solution enum per accepted range 每个可接受范围的解决方案枚举

enum EnemyStatus
{
    Ok
}

enum UserStatus
{
    Ok,
    Teleporting
}

Solution constraint at the type level 类型级别的解决方案约束

class Enemy : IStatusable {

  public Status CurrentStatus {
    get {return _status;}
    set {if(value == Status.Teleporting) throw new NotSupportedException();
         this._status = value;
    }}
}

interface IStatusable {
   Status CurrentStatus {get;set;}
}

it is possible to combine multiple values in a single enum field if enum is marked with [Flags] attribute: 如果使用[Flags]属性标记枚举,则可以在单个枚举字段中组合多个值:

[Flags]
enum Status
{
    Ok = 0,
    Teleporting = 1,
    Flying = 2,
    Dancing = 4
}

then declare interface with one enum property: 然后使用一个枚举属性声明接口:

interface Base
{
    Status Abilities { get; }
}

class Player : Base
{
    public Status Abilities { get; set; }
}

and test objects for different values like this: 并测试对象的不同值,如下所示:

var p = new Player();
p.Abilities = Status.Ok | Status.Teleporting;

bool canTeleport = p.Abilities.HasFlag(Status.Teleporting);
bool canFly = p.Abilities.HasFlag(Status.Flying);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM