【问题标题】:What are enum Flags in TypeScript?TypeScript 中的枚举标志是什么?
【发布时间】:2016-09-07 00:45:54
【问题描述】:

我正在使用 this ebook 作为参考来学习 TypeScript。我检查了TypeScript Official Documentation,但没有找到有关枚举标志的信息。

【问题讨论】:

  • 您参考的文档中给出的FileAccess 示例就是一个例子。您引用的电子书中的页面有一个名为“枚举作为标志”的部分,它描述了您正在寻找的内容,比下面的答案更详细,包括如何使用|= 等添加标志。

标签: typescript


【解决方案1】:

它们是一种有效存储和表示布尔值集合的方法。

例如,取这个标志枚举:

enum Traits {
    None = 0,
    Friendly = 1 << 0, // 0001 -- the bitshift is unnecessary, but done for consistency
    Mean = 1 << 1,     // 0010
    Funny = 1 << 2,    // 0100
    Boring = 1 << 3,   // 1000
    All = ~(~0 << 4)   // 1111
}

而不是只能像这样表示单个值:

let traits = Traits.Mean;

我们可以在一个变量中表示多个值:

let traits = Traits.Mean | Traits.Funny; // (0010 | 0100) === 0110

然后分别测试它们:

if ((traits & Traits.Mean) === Traits.Mean) {
    console.log(":(");
}

【讨论】:

  • let traits = Traits.Mean | Traits.Funny 现在traits 变量将有多个值,如何做相反的?我会有6如何转换成Traits.Mean | Traits.Funny
  • 特质 &= ~Traits.Funny;会让它变得刻薄而不是有趣。
  • 当你有超过 6 个标志时,同样的语法是否适用?
  • 刻薄友好?这怎么可能?玩笑不谈,但答案很好。
  • @Nieksa 是的,同样的答案也适用。唯一的限制是底层类型的大小(以位为单位),它是一个 32 位整数(因此支持 32 个标志)。这与 C# 之类的语言不太一样,后者的基础类型可以简单地更改。在 js/ts 中,所有按位运算的行为都好像值大小是 32 位(即使实际上是 64 位),因此需要一个特殊的库来处理 64 位 enum
【解决方案2】:

官方文档有这个例子,我会添加一些对使用 enum 和 flags 至关重要的细节。

enum FileAccess {
    None,
    Read    = 1 << 1,
    Write   = 1 << 2,
}

在 TypeScript 中,您可以直接使用 = 赋值

let x:FileAccess = FileAccess.Read;

但这可能会覆盖以前的值。要解决这个问题,您可以使用 |= 附加标志。

x |= FileAccess.Write;

此时,变量x为读写。您可以使用与号和波浪号删除值:

x &= ~FileAccess.Read;

最后,您可以比较一下是否将其中一个值设置为变量。接受的答案是不正确的。它不应该只使用 & 符号,还应该使用 === 检查所需的值。原因是 & 号返回一个数字,而不是布尔值。

console.log(FileAccess.Write === (x & FileAccess.Write)); // Return true
console.log(FileAccess.Read === (x & FileAccess.Read)); // Return false

【讨论】:

  • 感谢您指出这个例子。这很容易与现实世界的问题联系起来。
【解决方案3】:

你可以check this solution,添加一些infra,但是如果你经常使用它真的很好,用法是这样的:

let FlaggedExample: IFlaggedEnum = FlaggedEnum.create(Example, 1 << 2);  // class definition

let example = new FlaggedExample(3); // Alpha,Beta instance of the class
export module FlaggedEnum {
    "use strict";
    export interface IFlaggedEnumGenerator {
        (_enum: any, _max: number): IFlaggedEnum;
    }

    export interface IFlaggedEnum {
        (val: IFlaggedEnum): void;
        (val: number): void;
        (val: string): void;

        /** array of the individual enum flags that represent the value 
         */
        toArray(): IFlaggedEnum[];

        /** does this instance contain all the flags of the value 
         */
        contains(val: IFlaggedEnum): boolean;
        contains(val: number): boolean;
        contains(val: string): boolean;

        /** adds the flags to the value and returns a new instance 
         */
        add(val: IFlaggedEnum): IFlaggedEnum;
        add(val: number): IFlaggedEnum;
        add(val: string): IFlaggedEnum;

        /** removes the flags from the value and returns a new instance 
         */
        remove(val: IFlaggedEnum): IFlaggedEnum;
        remove(val: number): IFlaggedEnum;
        remove(val: string): IFlaggedEnum;

        /** returns an instance containing all intersecting flags 
         */
        intersect(val: IFlaggedEnum): IFlaggedEnum;
        intersect(val: number): IFlaggedEnum;
        intersect(val: string): IFlaggedEnum;

        /** does the two instances equal each other 
         */
        equals(val: IFlaggedEnum): boolean;
        equals(val: number): boolean;
        equals(val: string): boolean;

    }

    /** create a class definition for a Flagged Enum
     * @method create
     * @param _enum {enum} The enum definition being exteded
     * @param _max {number} the maximum possible value of the enum being extended
     * @returns {IFlaggedEnum} the class definition for the provided enum
     */
    export var create: IFlaggedEnumGenerator = function (_enum: any, _max: number): IFlaggedEnum {

        var base: any = _enum,
            max: number = _max;

        var Base: IFlaggedEnum = <any>function (val: any): void {
            if (typeof (val) === "string") {
                val = base[val];
            }
            this.value = val + 0;
        };

        var proto: any = Base.prototype;

        proto.valueOf = function (): number { return <number>this.value; };
        proto.toString = function (): string {
            var list: string[] = [];
            for (var i: number = 1; i < max; i = i << 1) {
                if ((this.value & i) !== 0) {
                    list.push(base[i]);
                }
            }
            return list.toString();
        };

        proto.toArray = function (): IFlaggedEnum[] {
            var list: IFlaggedEnum[] = [];
            for (var i: number = 1; i < max; i = i << 1) {
                if ((this.value & i) !== 0) {
                    list.push(new Base(i));
                }
            }
            return list;
        };

        proto.contains = function (val: any): boolean {
            if (typeof (val) === "string") {
                val = base[val];
            }
            return (this.value & val) === (val + 0);
        };

        proto.add = function (val: any): IFlaggedEnum {
            if (typeof (val) === "string") {
                val = base[val];
            }
            return new Base(this.value | val);
        };

        proto.remove = function (val: any): IFlaggedEnum {
            if (typeof (val) === "string") {
                val = this.base[val];
            }
            return new Base((this.value ^ val) & this.value);
        };

        proto.intersect = function (val: any): IFlaggedEnum {
            if (typeof (val) === "string") {
                val = base[val];
            }
            var final: number = 0;
            for (var i: number = 1; i < max; i = (i << 1)) {
                if ((this.value & i) !== 0 && (val & i) !== 0) {
                    final += i;
                }
            }
            return new Base(final);
        };

        proto.equals = function (val: any): boolean {
            if (typeof (val) === "string") {
                val = base[val];
            }
            return this.value === (val + 0);
        };

        return Base;

    };
}

【讨论】:

    【解决方案4】:

    您可以只使用位值来定义您的枚举

    enum FileAccess {
        None    = 0,
        Read    = 1,
        Write   = 2,
        Active  = 4,
        Failed  = 8
    }
    

    【讨论】:

    • 你没看错,但是这个问题在 5 年前就已经回答过了,在这里分配位移值是更易于维护的解决方案。
    【解决方案5】:

    标志允许您检查一组条件中的某个条件是否为真。这是各种其他编程语言中常见的编程模式,例如这是一个关于 C# 的示例:Using Bitwise operators on flags

    【讨论】:

    • 但问题是关于 TypeScript,特别是在枚举上下文中使用标志。
    【解决方案6】:
    enum Info{
        None = 0,
        glass= 1 << 0, // 0001 -- the bitshift is unnecessary, but done for consistency
        plastic= 1 << 1,     // 0010
    
    }
    

    【讨论】:

      猜你喜欢
      • 2018-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多