原文:http://www.blogcn.com/User8/flier_lu/index.html?id=3236734


    为了在一个标志字段中保存多种类型的标志,C 语言中定常见模式之一,是先定义一个 XXX_MASK,再定义一个 XXX_SHIFT,然后通过移位操作定义这段位上的标志,如 WinCrypt.h 中定义证书存储位置标志时,将位置标志位放在高16位中:
CS0019: C#中移位操作符的语法陷阱// Includes flags and location
CS0019: C#中移位操作符的语法陷阱
#define CERT_SYSTEM_STORE_MASK                  0xFFFF0000
CS0019: C#中移位操作符的语法陷阱
CS0019: C#中移位操作符的语法陷阱
// Location of the system store:
CS0019: C#中移位操作符的语法陷阱
#define CERT_SYSTEM_STORE_LOCATION_MASK         0x00FF0000
CS0019: C#中移位操作符的语法陷阱
#define CERT_SYSTEM_STORE_LOCATION_SHIFT        16
CS0019: C#中移位操作符的语法陷阱
CS0019: C#中移位操作符的语法陷阱
//  Registry: HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE
CS0019: C#中移位操作符的语法陷阱
#define CERT_SYSTEM_STORE_CURRENT_USER_ID       1
CS0019: C#中移位操作符的语法陷阱
#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ID      2
CS0019: C#中移位操作符的语法陷阱
CS0019: C#中移位操作符的语法陷阱
//CS0019: C#中移位操作符的语法陷阱
CS0019: C#中移位操作符的语法陷阱

CS0019: C#中移位操作符的语法陷阱
#define CERT_SYSTEM_STORE_CURRENT_USER          
CS0019: C#中移位操作符的语法陷阱    (CERT_SYSTEM_STORE_CURRENT_USER_ID 
<< CERT_SYSTEM_STORE_LOCATION_SHIFT)
CS0019: C#中移位操作符的语法陷阱
#define CERT_SYSTEM_STORE_LOCAL_MACHINE         
CS0019: C#中移位操作符的语法陷阱    (CERT_SYSTEM_STORE_LOCAL_MACHINE_ID 
<< CERT_SYSTEM_STORE_LOCATION_SHIFT)
CS0019: C#中移位操作符的语法陷阱
  
    而在 C# 中一般使用以 FlagsAttribute 标记后的 Enum 来模拟类似语义,如

CS0019: C#中移位操作符的语法陷阱[Flags]
CS0019: C#中移位操作符的语法陷阱
public enum CertSystemStoreFlag : uint
    
    FlagsAttribute 标记使得此 Enum 能够以位域(bit field)形式进行操作,既有 Flags 的类型安全特性,又有进行位操作的灵活性。同时还能定义此 Enum 的基本类型,如上面指定的 uint,以最大限度兼容现有 C 代码。
    
    不过这样组合使用 Enum 的多个特性时有一个小小的语法陷阱,不能将 C# 完全等同于 C++ 的语法,例如要这样照搬 C++ 定义语法:
CS0019: C#中移位操作符的语法陷阱[Flags]
CS0019: C#中移位操作符的语法陷阱
public enum CertSystemStoreFlag : uint
    
    编译时就会获得一个让人困惑的警告消息:
以下为引用:

CS0019: Operator '<<' cannot be applied to operands of type 'uint' and 'uint'

    
    以一个 C++ 背景的程序员角度来看,实在是无法想像为什么移位操作竟然不能对 uint 进行处理,不过在 C# 中这恰恰是语法所要求的。ECMA-334 C# Language Specification 的第 14.8 节 Shift operators 是这样定义 C# 的移位操作符的:
以下为引用:

The << and >> operators are used to perform bit shifting operations. 
  
    shift-expression : 
        additive-expression 
        shift-expression << additive-expression 
        shift-expression >> additive-expression 
        
...

The predefined shift operators are listed below. 

    2 Shift left: 

        int operator <<(int x, int count);  
        uint operator <<(uint x, int count);  
        long operator <<(long x, int count);  
        ulong operator <<(ulong x, int count);  

    3 The << operator shifts x left by a number of bits computed as described below. 4 The high-order bits outside the range of the result type of x are discarded, the remaining bits are shifted left, and the low-order empty bit positions are set to zero. 
    
    5 Shift right: 
    
        int operator >>(int x, int count);  
        uint operator >>(uint x, int count);  
        long operator >>(long x, int count);  
        ulong operator >>(ulong x, int count);  
    
    6 The >> operator shifts x right by a number of bits computed as described below. 7 When x is of type int or long, the low-order bits of x are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero if x is non-negative and set to one if x is negative. 8 When x is of type uint or ulong, the low-order bits of x are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero.        

    
    可以看到,双目移位操作符的两个参数实际上是不一样的,操作符左的参数可以是有/无符号的int和long,但操作符右边的则都是 int。因此上面那个错误的表达式中,移位操作符右边数字应该被显式转换为 int 来符合 C# 的语法要求。
CS0019: C#中移位操作符的语法陷阱[Flags]
CS0019: C#中移位操作符的语法陷阱
public enum CertSystemStoreFlag : uint
}

        
    呵呵,虽然比较别扭,但没办法,谁让 C# 定义得这么严谨呢 :P

btw: 感谢 Junfeng Zhang 帮忙指出问题所在 CS0019: C#中移位操作符的语法陷阱

相关文章:

  • 2022-12-23
  • 2021-10-16
  • 2021-08-17
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2021-07-31
  • 2022-01-06
  • 2021-11-20
相关资源
相似解决方案