2009-09-10 16 views
20

Với trường hợp tôi có một đối tượng có thể ở một hoặc nhiều trạng thái đúng/sai, tôi luôn hơi mờ về lý do tại sao các lập trình viên thường sử dụng cờ + bitmask thay vì chỉ sử dụng một vài giá trị boolean.Tại sao sử dụng cờ + bitmask thay vì một loạt các boolean?

Toàn bộ khuôn khổ .NET. Không chắc chắn nếu đây là ví dụ tốt nhất, nhưng khuôn khổ .NET có những điều sau đây:

public enum AnchorStyles 
{ 
    None = 0, 
    Top = 1, 
    Bottom = 2, 
    Left = 4, 
    Right = 8 
} 

Vì vậy, cho một kiểu neo, chúng ta có thể sử dụng bitmask để tìm ra trạng thái nào được chọn. Tuy nhiên, có vẻ như bạn có thể thực hiện điều tương tự với lớp/cấu trúc AnchorStyle với các thuộc tính bool được xác định cho mỗi giá trị có thể, hoặc một mảng các giá trị enum riêng lẻ.

Tất nhiên, lý do chính cho câu hỏi của tôi là tôi tự hỏi liệu tôi có nên làm theo một thực hành tương tự với mã của riêng tôi hay không.

Vì vậy, tại sao sử dụng phương pháp này?

  • Ít tiêu thụ bộ nhớ? (nó không dường như như nó sẽ tiêu thụ ít hơn một mảng/struct của bools)
  • Hiệu suất stack/heap tốt hơn so với cấu trúc hoặc mảng?
  • Hoạt động so sánh nhanh hơn? Thêm/xóa giá trị nhanh hơn?
  • Thuận tiện hơn cho nhà phát triển đã viết nó?
+2

Không phải là tôi thấy rằng một đối số mạnh mẽ, nhưng nó ** không ** tiêu thụ ít bộ nhớ hơn. Nó sử dụng hết một int (4 byte) trong khi mỗi bool sử dụng hết một byte. Vì vậy, 4 bools sử dụng lên giống như một int. 32 bool sử dụng hết 32 byte, trong khi tất cả những bools có thể ở trong cùng một enum. Và nếu bạn đi đến đường dẫn không được đề nghị, bạn có thể tạo ra enums dài 8 byte (sizeof (long)). –

+0

Cảm ơn bạn đã làm rõ điều đó. Nó dẫn tôi đến bài đăng này: http://stackoverflow.com/questions/294905/why-in-net-system-boolean-takes-4-byte –

+0

Vì vậy, từ các phản ứng rõ ràng là các cờ enum có trọng lượng nhẹ hơn các cấu trúc/mảng của bools về bộ nhớ. Tuy nhiên, có vẻ như có một số lớp khung công tác .NET phù hợp với nhiệm vụ, chẳng hạn như BitVector32 hoặc BitArray. Điều gì về một cấu trúc sử dụng một BitVector32 (được hỗ trợ bởi một uint) để lưu trữ và cung cấp các thuộc tính nhận/đặt bit (như bools) tại các chỉ mục cụ thể? Windows Forms dường như làm điều này. Thêm mã cho nhà phát triển, nhưng có vẻ như nó sẽ hoạt động tốt và việc đóng gói sẽ giúp người tiêu dùng API hạ lưu dễ sử dụng hơn. hmm? –

Trả lời

13

Đó là cách truyền thống để giảm mức sử dụng bộ nhớ. Vì vậy, vâng, nó khá lỗi thời trong C# :-)

Là một kỹ thuật lập trình, nó có thể là lỗi thời trong hệ thống hiện nay, và bạn muốn được khá ổn để sử dụng một mảng của bools, nhưng ...

Thật nhanh để so sánh các giá trị được lưu trữ dưới dạng bitmap.Sử dụng toán tử logic AND và OR và so sánh 2 ints kết quả.

Nó sử dụng ít bộ nhớ đáng kể hơn. Đặt tất cả 4 giá trị mẫu của bạn vào một bitmap sẽ sử dụng một nửa byte. Sử dụng một mảng các bool, rất có thể sẽ sử dụng một vài byte cho đối tượng mảng cộng với một từ dài cho mỗi bool. Nếu bạn phải lưu trữ một triệu giá trị, bạn sẽ thấy chính xác lý do tại sao một phiên bản bitmask là cấp trên.

Dễ quản lý hơn, bạn chỉ phải đối phó với một giá trị số nguyên duy nhất, trong khi một mảng các bool sẽ lưu trữ khá khác nhau trong cơ sở dữ liệu.

Và, do bố cục bộ nhớ, nhanh hơn nhiều ở mọi khía cạnh so với mảng. Nó gần như nhanh bằng cách sử dụng một số nguyên 32 bit. Chúng ta đều biết rằng đó là nhanh như bạn có thể nhận được cho các hoạt động trên dữ liệu.

1

Đó là tốc độ và hiệu quả. Về cơ bản tất cả các bạn đang làm việc với là một int duy nhất.

if ((flags & AnchorStyles.Top) == AnchorStyles.Top) 
{ 
    //Do stuff 
} 
+0

Đó là một câu trả lời khá cao. Bạn có thể cụ thể về những hoạt động nào nhanh hơn/hiệu quả hơn không và tại sao? Hoặc liên kết tới bài viết biện minh cho khiếu nại của bạn? –

+0

Tôi có thực sự cần cung cấp cho bạn bằng chứng rằng làm việc với các kiểu gốc và các biểu thức logic đơn giản là nhanh và hiệu quả không? – ChaosPandion

+0

Đừng quên thứ tự các hoạt động. Bạn phải đặt dấu ngoặc đơn xung quanh hoạt động bitwise ở đó. –

12
  • Dễ dàng thiết lập nhiều cờ trong bất kỳ thứ tự.

  • Dễ lưu và nhận được một serie 0101011 vào cơ sở dữ liệu.

+2

Lưu ý rằng ngay cả khi các cột riêng biệt, SQL Server sẽ tối ưu hóa chúng thành một byte đơn: http://msdn.microsoft.com/en-us/library/ms177603.aspx – AaronLS

6

Trong số những thứ khác, dễ dàng thêm ý nghĩa bit mới vào bitfield hơn để thêm các giá trị boolean mới vào một lớp. Nó cũng dễ dàng hơn để sao chép một bitfield từ một thể hiện này sang một thể hiện khác so với một loạt các boolean.

+0

Dường như với tôi rằng việc thêm các giá trị boolean vào một lớp là dễ dàng như: bool newState; Về việc sao chép, có vẻ dễ dàng sao chép cấu trúc. –

+0

@Winston: Thay đổi định dạng tuần tự hóa và serializers tốt chấp nhận các giá trị mặc định cho dữ liệu cũ và khi các phiên bản cũ không vứt bỏ các trường không xác định khó tìm. Giao diện nhị phân thay đổi, có thể gây ra một chuỗi các cập nhật cần thiết và yêu cầu hỗ trợ verisoning đầy đủ cho cấu trúc. (Tất nhiên, * hợp đồng * sẽ phải nêu rõ "các bit không xác định bị bỏ qua" hoặc "các bit không xác định gây ra lỗi"). Ngoài ra, trên cấp độ triển khai, xử lý chúng như một toàn bộ IS dễ dàng hơn. – peterchen

+1

@Winston nếu bạn đã tạo API? Sau đó, tất cả mọi người có thể nâng cấp lên phiên bản mới của bạn sẽ phải thay đổi mã đó vì một bool mới đã được thêm vào một phương thức. Trong khi nếu nó là một enum sau đó không có thay đổi trên có kết thúc phải được thực hiện để giữ cho có cùng một mã bằng cách sử dụng nó. Đó là lý do tại sao.NET framework ủng hộ enums trên booleans. –

3

Thực ra, nó có thể có hiệu suất tốt hơn, chủ yếu nếu enum của bạn xuất phát từ một byte. Trong trường hợp cực đoan đó, mỗi giá trị enum sẽ được biểu diễn bằng một byte, chứa tất cả các kết hợp, lên tới 256. Có quá nhiều kết hợp có thể với các phép toán sẽ dẫn đến 256 byte.

Nhưng, ngay cả khi đó, tôi không nghĩ đó là lý do thực sự. Lý do tôi thích những thứ đó là sức mạnh mà C# mang lại cho tôi để xử lý những enums đó. Tôi có thể thêm một vài giá trị với một biểu thức duy nhất. Tôi cũng có thể loại bỏ chúng. Tôi thậm chí có thể so sánh nhiều giá trị cùng một lúc với một biểu thức duy nhất bằng cách sử dụng enum. Với boolean, mã có thể trở thành, hãy nói, tiết hơn.

+3

Có 256 kết hợp, nhưng chỉ có 8 lá cờ. Đừng nhầm lẫn chúng. – Dykam

+0

Xin lỗi, tiếng Anh của tôi không đủ rõ ràng. Bạn đúng. –

+1

256 kết hợp sử dụng bool? Đó là 8 giá trị bool. 8 giá trị bool không phải là 256 byte. – CoperNick

5

Nó cũng có thể làm cho phương pháp rõ ràng hơn. Hãy tưởng tượng một phương pháp với 10 bool so với 1 Bitmask.

2

Tôi sẽ đề nghị không bao giờ sử dụng cờ enum trừ khi bạn đang đối phó với một số giới hạn bộ nhớ khá nghiêm trọng (không có khả năng). Bạn nên luôn viết mã được tối ưu hóa để bảo trì.

Có một số thuộc tính boolean giúp đọc và hiểu mã dễ dàng hơn, thay đổi giá trị và cung cấp nhận xét Intellisense chưa kể đến việc giảm khả năng lỗi. Nếu cần thiết, bạn luôn có thể sử dụng một trường cờ enum trong nội bộ, chỉ cần đảm bảo rằng bạn phơi bày cài đặt/nhận các giá trị với các thuộc tính boolean.

3

Raymond Chen có a blog post on this subject.

Chắc chắn, bitfields tiết kiệm bộ nhớ dữ liệu, nhưng bạn phải cân bằng nó so với chi phí kích thước mã, debuggability, và giảm đa luồng.

Như những người khác đã nói, thời gian của nó phần lớn là quá khứ. Thật hấp dẫn để vẫn làm điều đó, gây khó chịu một chút là thú vị và đẹp mắt, nhưng nó không còn hiệu quả nữa, nó có những hạn chế nghiêm trọng về bảo trì, nó không chơi độc đáo với cơ sở dữ liệu, và trừ khi bạn đang làm việc trong một thế giới nhúng, bạn có đủ bộ nhớ.

+2

Raymond đang nói về bitfields, chứ không phải bitmap. – gbjbaanb

1

Từ góc độ Mô hình miền, nó chỉ mô hình thực tế tốt hơn trong một số trường hợp. Nếu bạn có ba boolean như AccountIsInDefault và IsPreferredCustomer và RequireSalesTaxState, thì sẽ không có ý nghĩa gì khi thêm chúng vào một liệt kê cờ trang trí đơn, vì chúng không phải là ba giá trị riêng biệt cho cùng một phần tử mô hình miền.

Nhưng nếu bạn có một tập hợp các phép toán như:

[Flags] enum AccountStatus {AccountIsInDefault=1, 
     AccountOverdue=2 and AccountFrozen=4} 

hoặc

[Flags] enum CargoState {ExceedsWeightLimit=1, 
     ContainsDangerousCargo=2, IsFlammableCargo=4, 
     ContainsRadioactive=8} 

Sau đó, nó rất hữu ích để có thể lưu trữ các tổng trạng thái của tài khoản, (hoặc hàng hóa) trong MỘT biến ... đại diện cho MỘT Phần tử tên miền có giá trị có thể đại diện cho bất kỳ kết hợp các trạng thái có thể có nào.

1
  1. Space hiệu quả - 1 chút
  2. Thời gian hiệu quả - so sánh chút được xử lý một cách nhanh chóng bằng phần cứng.
  3. Độc lập về ngôn ngữ - nơi dữ liệu có thể được xử lý bởi một số chương trình khác nhau mà bạn không cần phải lo lắng về việc triển khai booleans trên các ngôn ngữ/nền tảng khác nhau.

Hầu hết thời gian, đây không phải là giá trị của sự cân bằng về bảo trì. Tuy nhiên, có những lúc nó rất hữu ích:

  1. giao thức mạng - sẽ có một tiết kiệm lớn trong giảm kích thước của thông điệp
  2. Legacy phần mềm - một khi tôi đã có thêm một số thông tin để truy tìm vào một số phần mềm kế thừa.

Chi phí để sửa đổi tiêu đề: hàng triệu đô la và nhiều năm nỗ lực. Chi phí để shoehorn thông tin thành 2 byte trong tiêu đề mà không được sử dụng: 0.

Tất nhiên, có thêm chi phí trong mã truy cập và thao tác thông tin này, nhưng chúng được thực hiện bởi các chức năng anyways vì vậy một khi bạn đã có những người truy cập xác định nó không thể duy trì ít hơn so với sử dụng Booleans.

+1

1. Hiệu quả không gian chỉ áp dụng trong môi trường rất dày đặc hoặc có giới hạn; 2. Hiệu quả thời gian phụ thuộc vào việc sử dụng hiệu quả mặt nạ (và chắc chắn * không * nhanh hơn so với mặt nạ và so sánh một bit so với việc so sánh một giá trị boolean đơn); 3. Không áp dụng, sử dụng kiểu boolean không chính xác đang sử dụng kiểu boolean không chính xác. – user2864740

Các vấn đề liên quan