2008-09-02 37 views
133

Tôi có một enum cờ dưới đây.Cách so sánh cờ trong C#?

[Flags] 
public enum FlagTest 
{ 
    None = 0x0, 
    Flag1 = 0x1, 
    Flag2 = 0x2, 
    Flag3 = 0x4 
} 

Tôi không thể đưa ra tuyên bố nếu được đánh giá là đúng.

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2; 

if (testItem == FlagTest.Flag1) 
{ 
    // Do something, 
    // however This is never true. 
} 

Làm cách nào để tôi thực hiện điều này?

+0

Đúng tôi nếu tôi là sai, là 0 thích hợp để được được sử dụng làm giá trị cờ? – Roylee

+3

@Roylee: 0 là chấp nhận được và bạn nên có cờ "Không" hoặc "Không xác định" để kiểm tra không có cờ nào được đặt. Nó không cần thiết, nhưng đó là một thực hành tốt. Điều quan trọng cần nhớ về điều này được Leonid chỉ ra trong câu trả lời của anh ta. – Andy

+4

@Roylee Nó thực sự được đề nghị bởi Microsoft để cung cấp một lá cờ 'None' với giá trị bằng không. Xem http://msdn.microsoft.com/en-us/library/vstudio/ms229058(v=vs.100).aspx – ThatMatthew

Trả lời

289

Trong .NET 4 có một phương pháp mới Enum.HasFlag. Điều này cho phép bạn viết:

if (testItem.HasFlag(FlagTest.Flag1)) 
{ 
    // Do Stuff 
} 

dễ đọc hơn nhiều, IMO.

.NET nguồn chỉ ra rằng đây thực hiện các logic tương tự như các câu trả lời được chấp nhận:

public Boolean HasFlag(Enum flag) { 
    if (!this.GetType().IsEquivalentTo(flag.GetType())) { 
     throw new ArgumentException(
      Environment.GetResourceString(
       "Argument_EnumTypeDoesNotMatch", 
       flag.GetType(), 
       this.GetType())); 
    } 

    ulong uFlag = ToUInt64(flag.GetValue()); 
    ulong uThis = ToUInt64(GetValue()); 
    // test predicate 
    return ((uThis & uFlag) == uFlag); 
} 
+22

Ah, cuối cùng là một cái gì đó ra khỏi hộp. Điều này thật tuyệt, tôi đã chờ đợi tính năng tương đối đơn giản này trong một thời gian dài. Rất vui khi họ quyết định bỏ nó vào. –

+8

Lưu ý, tuy nhiên, câu trả lời dưới đây cho thấy các vấn đề hiệu suất với phương pháp này - đây có thể là vấn đề đối với một số người. Hạnh phúc không phải cho tôi. –

+1

Việc xem xét hiệu suất cho phương pháp này là quyền anh vì nó lấy các đối số như một thể hiện của lớp 'Enum'. –

4

Đối với các thao tác bit, bạn cần sử dụng toán tử bitwise.

này nên làm như lừa:

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
    // Do something, 
    // however This is never true. 
} 

Edit: cố định của tôi nếu kiểm tra - Tôi trượt trở lại vào C của tôi/C++ cách (nhờ Ryan Farley cho trỏ nó ra)

171
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
    // Do something 
} 

(testItem & FlagTest.Flag1) là hoạt động bitwise AND.

FlagTest.Flag1 tương đương với 001 với enum enum. Bây giờ chúng ta hãy nói testItem có Flag1 và Flag2 (vì vậy nó Bitwise 101):

001 
&101 
---- 
    001 == FlagTest.Flag1 
+2

Logic chính xác ở đây là gì? Tại sao vị từ phải được viết như thế này? –

+3

@ IanR.O'Brien Flag1 | Gắn cờ 2 dịch thành 001 hoặc 010 tương tự như 011, bây giờ nếu bạn làm một sự bình đẳng của điều đó 011 == Flag1 hoặc dịch 011 == 001, trả về false luôn. Bây giờ nếu bạn làm một bitwise AND với Flag1 thì nó chuyển thành 011 AND 001 trả về 001 bây giờ làm bình đẳng trả về true, bởi vì 001 == 001 – pqsk

+0

Đây là giải pháp tốt nhất vì HasFlags tốn nhiều tài nguyên hơn. Và hơn thế nữa những gì HasFlags đang làm, ở đây được thực hiện bởi trình biên dịch –

7
if((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
... 
} 
4

Hãy thử điều này:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
    // do something 
} 
Về cơ bản, mã của bạn được hỏi nếu có cả cờ thiết là giống như có một bộ cờ, rõ ràng là sai. Mã ở trên sẽ chỉ để lại bộ cờ Flag1 nếu được đặt ở tất cả, sau đó so sánh kết quả này với Flag1.

21

Tôi thiết lập một phương pháp tiện ích để thực hiện: related question.

Về cơ bản:

public static bool IsSet(this Enum input, Enum matchTo) 
{ 
    return (Convert.ToUInt32(input) & Convert.ToUInt32(matchTo)) != 0; 
} 

Sau đó, bạn có thể làm:

FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2; 

if(testItem.IsSet (FlagTests.Flag1)) 
    //Flag1 is set 

Ngẫu ước tôi sử dụng cho sự đếm là số ít cho tiêu chuẩn, số nhiều cho cờ. Bằng cách đó bạn biết từ tên enum cho dù nó có thể chứa nhiều giá trị.

+0

Đây phải là nhận xét nhưng tôi là người dùng mới có vẻ như tôi chưa thể thêm nhận xét ... public static bool isset (đầu vào này Enum, Enum matchTo) { return (Convert.ToUInt32 (đầu vào) & Convert.ToUInt32 (matchTo)) = 0!; } Có cách nào tương thích với bất kỳ loại enum nào không (vì ở đây nó sẽ không hoạt động nếu enum của bạn thuộc loại UInt64 hoặc có thể có giá trị âm)? – user276648

+0

Điều này khá dư thừa với Enum.HasFlag (Enum) (có sẵn trong .net 4.0) – PPC

+1

@PPC Tôi sẽ không nói chính xác dư thừa - nhiều người đang phát triển trên các phiên bản cũ của khung công tác. Mặc dù vậy, bạn là người dùng .Net 4 nên sử dụng phần mở rộng 'HasFlag' để thay thế. – Keith

3

Về chỉnh sửa. Bạn không thể làm cho nó đúng. Tôi đề nghị bạn bọc những gì bạn muốn vào một lớp khác (hoặc phương pháp mở rộng) để có được gần gũi hơn với cú pháp bạn cần.

ví dụ:

public class FlagTestCompare 
{ 
    public static bool Compare(this FlagTest myFlag, FlagTest condition) 
    { 
     return ((myFlag & condition) == condition); 
    } 
} 
77

Đối với những người gặp khó khăn khi hình dung những gì đang xảy ra với các giải pháp chấp nhận (đó là này),

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{ 
    // Do stuff. 
} 

testItem (theo câu hỏi) được định nghĩa là,

testItem 
= flag1 | flag2 
= 001 | 010 
= 011 

Sau đó, trong câu lệnh if, phía bên trái của so sánh là,

(testItem & flag1) 
= (011 & 001) 
= 001 

Và đầy đủ câu lệnh if (mà trả về true nếu flag1 được thiết lập trong testItem),

(testItem & flag1) == flag1 
= (001) == 001 
= true 
19

Thêm một lời khuyên ... Không bao giờ làm việc kiểm tra nhị phân chuẩn với cờ mà giá trị là "0". Séc của bạn trên lá cờ này sẽ luôn đúng.

[Flags] 
public enum LevelOfDetail 
{ 
    [EnumMember(Value = "FullInfo")] 
    FullInfo=0, 
    [EnumMember(Value = "BusinessData")] 
    BusinessData=1 
} 

Nếu bạn nhị phân tham số đầu vào kiểm tra đối với FullInfo - bạn nhận được:

detailLevel = LevelOfDetail.BusinessData; 
bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo; 

bPRez sẽ luôn luôn đúng như bất cứ điều gì & 0 luôn == 0.


Thay vào đó, bạn chỉ cần kiểm tra xem giá trị của đầu vào là 0:

bool bPRez = (detailLevel == LevelOfDetail.FullInfo); 
+0

Tôi vừa sửa lỗi lỗi 0 cờ. Tôi nghĩ đây là lỗi thiết kế trong .NET framework (3.5) vì bạn cần biết giá trị cờ nào là 0 trước khi kiểm tra nó. – thersch

21

@ phil-Devaney

Lưu ý rằng ngoại trừ trong đơn giản nhất của các trường hợp, Enum.HasFlag mang một hình phạt hiệu quả nặng so với viết ra mã bằng tay. Hãy xem xét mã sau đây:

[Flags] 
public enum TestFlags 
{ 
    One = 1, 
    Two = 2, 
    Three = 4, 
    Four = 8, 
    Five = 16, 
    Six = 32, 
    Seven = 64, 
    Eight = 128, 
    Nine = 256, 
    Ten = 512 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     TestFlags f = TestFlags.Five; /* or any other enum */ 
     bool result = false; 

     Stopwatch s = Stopwatch.StartNew(); 
     for (int i = 0; i < 10000000; i++) 
     { 
      result |= f.HasFlag(TestFlags.Three); 
     } 
     s.Stop(); 
     Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms* 

     s.Restart(); 
     for (int i = 0; i < 10000000; i++) 
     { 
      result |= (f & TestFlags.Three) != 0; 
     } 
     s.Stop(); 
     Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*   

     Console.ReadLine(); 
    } 
} 

Hơn 10 triệu lần lặp lại, phương pháp mở rộng HasFlags mất 473 ms, so với 27 ms để thực hiện bitwise chuẩn.

+5

Thật vậy. Nếu bạn nhìn vào việc thực hiện HasFlag, bạn sẽ thấy rằng nó có một "GetType()" trên cả hai toán hạng, mà là khá chậm. Sau đó, nó "Enum.ToUInt64 (value.GetValue());" trên cả hai toán hạng trước khi thực hiện kiểm tra bitwise. – user276648

+0

Đây là một sự khác biệt hiệu suất rất lớn cho một chức năng rất nhỏ đạt được. Giá trị ngừng sử dụng HasFlag() – PPC

+1

Tôi chạy thử nghiệm của bạn nhiều lần và có ~ 500ms cho HasFlags và ~ 32ms cho bitwise. Trong khi vẫn còn một thứ tự của cường độ nhanh hơn với bitwise, HasFlags là một bậc độ lớn xuống từ thử nghiệm của bạn. (Chạy thử nghiệm trên Core i3 2.5GHz và .NET 4.5) – MarioVW

1

ngay cả khi không [Flags], bạn có thể sử dụng một cái gì đó như thế này

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2))!=0){ 
//.. 
} 

hoặc nếu bạn có một enum giá trị Zero,

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2))!=FlagTest.None){ 
//.. 
}