问题出发点http://topic.csdn.net/u/20100404/11/fa54404c-1510-4027-a799-1bc420c53444.html?14163
原文:    enum TU
    {
        A=0x01,
        B=0x04,
        C=0x05,
        D=0x15,
        E=0x0
    }TU t=TU.D;
t-=TU.A;//这是对的
t+=TU.A//错误
t=t+TU.A//错误
t=t-TU.A//错误-----------------------------------------
1.-=在枚举中的意义
二元 - 运算符是为所有数值类型和枚举类型预定义的,其功能是从第一个操作数中减去第二个操作数。
猜想:从整体上来看在此处t -= TU.A的应该是移除效果.
为什么会猜想是移除,原因要从enum的[FlagsAttribute]说起.
enum按位运算很常用,例如: FileAttributes.ReadOnly | FileAttributes.Hidden;
说到enum的按位运算,当定义的enum元素用 2 的幂(即 1、2、4、8 等)定义枚举常量。这意味着组合的枚举常量中的各个标志都不重叠.在使用ToString()时,才能枚举出所有的元素值.有以下例子:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            TU t =  TU.One| TU.Two | TU.Four;
            Console.WriteLine(((Int32)t).ToString());
            Console.WriteLine(t.ToString());
            t -= TU.One;//从t中移除TU.One
            Console.WriteLine(((Int32)t).ToString());
            Console.WriteLine(t.ToString());
        }
    }
    [FlagsAttribute]
    enum TU
    {
        One = 1,
        Two = 2,
        Four = 4,
        Eight = 8,
        Sixteen =16
    }
}运行结果

7
One,Two,Four
6
Two,Four
2.为什么不提供+=
a.从意义上来说+=和|=的作用相当.所以不必提供该操作.
b.实际上个人觉得+=(|=)比-=更容易产生不可预知的问题.例如FileAttributes.Normal和 FileAttributes.Hidden;FileAttributes.Normal标识文件未设置任何属性,FileAttributes.Hidden则是标识文件是隐藏的,两者相异.这其中有一些微妙的地方,下文将说到.3.enum的微妙之处
以下引自MSDN中<<对FlagsAttribute 和 Enum 的准则>>的第二条.
用 2 的幂(即 1、2、4、8 等)定义枚举常量。这意味着组合的枚举常量中的各个标志都不重叠。 
enum在实际应用中会有类似FileAttributes.Normal的情况.例如存在一个自定义的文件操作枚举FileOperations,假设该枚举存在一个值None ,该值是用于标识未指定其他值的情况.
该enum如下.        [FlagsAttribute]
        enum FileOperations
        {
            Read = 1,
            Write = 2,
            Delete = 4,
            Hidden = 8,
            None = 16
        }然后进行以下实验using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            FileOperations f = FileOperations.Delete | FileOperations.Read;
            Console.WriteLine("原始的值\r\n" + f.ToString());
            f -= FileOperations.Delete;
            Console.WriteLine("移除Delete后的值\r\n"+f.ToString());
            f -= FileOperations.Read;
            Console.WriteLine("移除Read后的值\r\n" + f.ToString());
        }        [FlagsAttribute]
        enum FileOperations
        {
            Read = 1,
            Write = 2,
            Delete = 4,
            Hidden = 8,
            None = 16,
        }
    }
}运行结果:

原始的值
Read,Delete
移除Delete后的值
Read
移除Read后的值
0
请按任意键继续. . .

这里我们可以看到,FileOperations f = FileOperations.Delete | FileOperations.Read依次移除后,状态应该是前面提到的该值是用于标识未指定其他值的情况.,即None,但实际上f并没得到我们想要的值.
然后我们将代码加经改进using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            FileOperations f = FileOperations.Delete | FileOperations.Read;
            Console.WriteLine("原始的值\r\n" + f.ToString());
            f -= FileOperations.Delete;
            Console.WriteLine("移除Delete后的值\r\n"+f.ToString());
            f -= FileOperations.Read;
            Console.WriteLine("移除Read后的值\r\n" + f.ToString());
        }        [FlagsAttribute]
        enum FileOperations
        {
            Read = 1,
            Write = 2,
            Delete = 4,
            Hidden = 8,
            None = 0,//这里是主要变化
        }
    }
}主要的变化是我们将None的值由16变成了0,运行结果如下:

原始的值
Read,Delete
移除Delete后的值
Read
移除Read后的值
None
请按任意键继续. . .

这样就达到了我们的目的.
假设我们需要扩展一个包含所有操作的值All,加以改进就行.using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            FileOperations f = FileOperations.Delete | FileOperations.Read | FileOperations.Write | FileOperations.Hidden;
            Console.WriteLine("原始的值\r\n" + f.ToString());
            f -= FileOperations.Delete;
            Console.WriteLine("移除Delete后的值\r\n"+f.ToString());
            f -= FileOperations.Read;
            Console.WriteLine("移除Read后的值\r\n" + f.ToString());
            f -= FileOperations.Write;
            Console.WriteLine("移除Write后的值\r\n" + f.ToString());
            f -= FileOperations.Hidden;
            Console.WriteLine("移除Hidden后的值\r\n" + f.ToString());
        }        [FlagsAttribute]
        enum FileOperations
        {
            None = 0,
            Read = 1,
            Write = 2,
            Delete = 4,
            Hidden = 8,
            All = 15//此处是关键
        }
    }
}运行结果:

原始的值
All
移除Delete后的值
Read, Write, Hidden
移除Read后的值
Write, Hidden
移除Write后的值
Hidden
移除Hidden后的值
None

注意上面的All不再是2的次幂.此前的None也不再是.All为前面所有值的和.None则为0;
这样的指定并没有破坏enum的位运算.
建义使用2的次幂做值的原因是因为1,2,4,8……2^n可以组合成任何1~2^n之间的数.如3=1+2,5=4+1,7=4+2+1……。
这样在做逻辑运算产生新值的时候会与原定义值重复。
而0和2^n+1不在此列。但微软似乎出于别的原因考虑,不推荐这样使用。FileAttributes 枚举使用的值是128。
4。意外的结果using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            FileOperations f = FileOperations.Hidden;
            Console.WriteLine("原始的值\r\n" + f.ToString());
            f -= FileOperations.Delete;
            Console.WriteLine("移除Delete后的值\r\n"+f.ToString());
        }        [FlagsAttribute]
        enum FileOperations
        {
            None = 0,
            Read = 1,
            Write = 2,
            Delete = 4,
            Hidden = 8,
            All = 15
        }
    }
}
虽然应用中不会从一个只包含Hidden的enum中减去一个Delete。,但这个结果着实让我吓一跳。上例从一个enum实例中移除一个值。原本f= Hidden;但移除Delete后,居然变成Delete了,因为8 - 4 = 4;
如果-=是移除的话,理论应该是None。-=是什么操作已经不重要了,不管它是什么,都带来了意料之中、情理之外的结果。
-=真的需要要达到一个移除操作的话。只需将要运算的变量进行一次|=运算,再执行-=即可。

解决方案 »

  1.   

                转成int,就行了。
                TU t = TU.D;
                t -= TU.A;
                t += (int)TU.A;
                t = t + (int)TU.A;
                t = t - (int)TU.A; 或者你重载一下+,-运算
      

  2.   


    这样使用枚举更多是臆断的吧。比较常见的写法是        enum FileOperations
            {
                Read = 1,
                Write,
                Delete,
                Hidden,
                None
            }
      

  3.   

    换句话说,或许我们可以首先不想当然地费尽力气小心翼翼地为每一个都分别指定一个特别别数值,就自然而然地去看看人家.net自动为枚举生成什么值?
      

  4.   

    值为2的幂是为了应用 FlagsAttribute 特性。
      

  5.   

    enum TU { A,B,C,D,E }TU t = TU.D;
    t -= TU.A; // 按我的理解,这应该是等价于 t = (TU)(t - TU.A);
      

  6.   

    enum TU { A,B,C,D,E }TU t = TU.D;
    t -= TU.A;           // 可以
    t = (TU)(t - TU.A);  // 可以,等价于上行
    t = t - TU.A;        // 错误
    // 类比:byte b = 78;
    b -= 5;              // 可以
    b = (byte)(b - 5);   // 可以,等价于上行
    b = b - 5;           // 错误
      

  7.   

    两个 Enum 类型的变量相减的结果为 int,这点和 byte 类型的变量相减类似。但两个 Enum 类型的变量不能相加,这和 byte 类型是不同的。两个枚举变量相减的意思就是它们之间相隔多远,结果是 int。
    两个枚举变量相加就没有明显的、自然的意义了,所以不允许。正如两个 DateTime 型的变量相减的意思是时间间隔,结果是 TimeSpan 型的变量,
    而两个 DateTime 型的变量相加没有意义,不允许。
      

  8.   

    两个 Enum 类型的变量相减的结果为 int,这点和 byte 类型的变量相减类似。但两个 Enum 类型的变量不能相加,这和 byte 类型是不同的。两个枚举变量相减的意思就是它们之间相隔多远,结果是 int。
    两个枚举变量相加就没有明显的、自然的意义了,所以不允许。正如两个 DateTime 型的变量相减的意思是时间间隔,结果是 TimeSpan 型的变量,
    而两个 DateTime 型的变量相加没有意义,不允许。 
     
      

  9.   

    楼主厉害?
    楼主的长篇大论的核心就是想说明枚举量的 -= 操作应该是移除效果。但从5、6、8楼的分析来看,两个枚举量相减的意义是它们的间隔,结果是 int 型的,再把它强制转换为枚举量,已经失去了意义(正因为把这个 int 量转换为枚举量没有实际的意义,微软在此处才不允许隐式转换,而需要强制转换:t = (TU)(t - TU.A);)。x -= y; 只不过是 x = x - y; 的便捷写法罢了。
      

  10.   

    枚举的实现机制注定了它们相减得到的不是一个Int,虽然得到的枚举本质上是Int,但显现出来的却是枚举特性,除非找不到匹配的情况..
      

  11.   


    我们就以 LZ 的 FileOperations 枚举为例,两个枚举量相减(a - b)的结果是 int,而不是枚举量,尽管相减的结果可以找到匹配(结果是 4,与 Delete 匹配)。using System;[Flags]
    enum FileOperations
    {
      Delete = 4,
      Hidden = 8,
    }class Program
    {
      static void Main()
      {
        FileOperations a = FileOperations.Hidden;
        FileOperations b = FileOperations.Delete;    object x = a - b; // 以下可以看出:结果是 int,而不是枚举。
        Console.WriteLine(x);                 // 输出 4
        Console.WriteLine(x.GetType());       // 输出 System.Int32
        Console.WriteLine((FileOperations)x); // 输出 Delete
      }
    }
      

  12.   

    想从枚举量 a 中移除枚举量 b,正确而简单的做法应该是:a &= ~b;想用 a -= b; 的办法移除 b,就必须先判断 a 是否含有 b,
    并且不是总是有效的,楼下说明。using System;[Flags]
    enum FileOperations
    {
      Delete = 4,
      Hidden = 8,
    }class Program
    {
      static void Main()
      {
        FileOperations a, b = FileOperations.Delete;
        
        // 想从 a 中移除 b,正确的做法如下:
        a = FileOperations.Hidden;
        a &= ~b;  // 不论 a 是否含有 b,都可以得到正确的结果
        Console.WriteLine(a); // 输出 Hideden,正确。    // 想从 a 中移除 b,不正确的做法如下:
        a = FileOperations.Hidden;
        a -= b;  // 如果 a 不含有 b,就不能得到正确的结果
        Console.WriteLine(a); // 输出 Delete,错误。    // 想从 a 中移除 b,另一种做法:
        a = FileOperations.Hidden;
        if (( a & b) == b) a -= b;  // 先判断 a 是否含有 b,再相减,可以。
        Console.WriteLine(a);       // 输出 Hideden,正确。
      }
    }
      

  13.   

    如果枚举量 a 只含有部分的 b,从 a 移除 b 正确而简单的做法仍然是:a &= ~b;a -= b; 就没有办法做到从 a 移除 b 了,无论是是否判断 a 含有 b。using System;[Flags]
    enum FileOperations
    {
      Read = 1,
      Write = 2,
      Delete = 4,
      Hidden = 8,
    }class Program
    {
      static void Main()
      {
        FileOperations a, b = FileOperations.Delete | FileOperations.Read;
        
        // 想从 a 中移除 b,正确的做法如下:
        a = FileOperations.Hidden | FileOperations.Delete;
        a &= ~b;              // 不论 a 是否部分含有 b,都正确。
        Console.WriteLine(a); // 输出 Hideden,正确。    // 想从 a 中移除 b,不正确的做法如下:
        a = FileOperations.Hidden | FileOperations.Delete;
        a -= b;                     // 如果 a 含有部分的 b,结果不知所云。
        Console.WriteLine(a);       // 输出 Read, Write, Delete,错误。    // 想从 a 中移除 b,另一种做法(错误):
        a = FileOperations.Hidden | FileOperations.Delete;
        if (( a & b) == b) a -= b;  // 先判断 a 是否完全含有 b,再相减。
        Console.WriteLine(a);       // 输出 Delete, Hidden,错误。
        // 想从 a 中移除 b,还一种做法(也不正确):
        a = FileOperations.Hidden | FileOperations.Delete;
        if (( a & b) != 0) a -= b;  // 先判断 a 是否部分含有 b,再相减。
        Console.WriteLine(a);       // 输出 Read, Write, Delete,也不正确。
      }
    }
      

  14.   

    总结:两个枚举量相减的结果是 int,意义是它们的间隔,
    再转换为枚举没有什么实际意义,只是在某些特定的情况下表现为移除的效果。
    所以 a -= b; 是不好的写法,因为把枚举量的间隔转换为枚举在很多情况下没意义。移除的正确而简单的写法是:a &= ~b;
      

  15.   

    两个枚举量相减的结果是有意义的,就是它们的间隔(结果是 int 型的,而不是枚举型的)。如果 a 和 b 是两个相同类型的枚举量,则表达式 a - b 是有意义的,如上所述。
    但 a -= b; 只不过是 a = (a的类型)(a - b); 的简便写法而已,把一个 int 型的量(枚举量的间隔)转换为枚举量,在很多情况下没意义。
      

  16.   

    en  xue xi zhong !!
      

  17.   

    FlagsAttribute不是什么新奇事物,.NET出生之前就有的概念...但是...“猜想:从整体上来看在此处t -= TU.A的应该是移除效果.”只是楼主的臆断...它仅仅是整数-操作而已,没有任何特殊之处...楼上空军的解释就能说明...注意MSDN中用于 FlagsAttribute 和 Enum 的准则...所以你的原文那段代码没有所谓对的错的是你理解上的错不是enum的错更和什么+=、-=没关系...
      

  18.   

    a &= ~b;这个去除功能不错
      

  19.   

    -可以用在两个enum比较大小上,不过+就没这个意义了,本来enum就不适合加减运算,唯一适合的运算就是位运算。
      

  20.   

    在使用FlagAttribute标记的枚举时不建议使用+和-,建议使用 | 和 ^
      

  21.   

    每天回帖即可获得10分可用分!回帖是种美德>>