Làm thế nào để bạn chuyển nhiều giá trị enum trong C #?


117

Đôi khi khi đọc mã C # của người khác, tôi thấy một phương thức sẽ chấp nhận nhiều giá trị enum trong một tham số duy nhất. Tôi luôn nghĩ nó khá gọn gàng, nhưng chưa bao giờ nhìn vào nó.

Vâng, bây giờ tôi nghĩ tôi có thể có nhu cầu về nó, nhưng không biết làm thế nào để

  1. thiết lập chữ ký phương thức để chấp nhận điều này
  2. làm việc với các giá trị trong phương thức
  3. xác định enum

để đạt được điều này.


Trong tình huống cụ thể của tôi, tôi muốn sử dụng System.DayOfWeek, được định nghĩa là:

[Serializable]
[ComVisible(true)]
public enum DayOfWeek
{ 
    Sunday = 0,   
    Monday = 1,   
    Tuesday = 2,   
    Wednesday = 3,   
    Thursday = 4,   
    Friday = 5,    
    Saturday = 6
}

Tôi muốn có thể chuyển một hoặc nhiều giá trị DayOfWeek cho phương thức của mình. Liệu tôi có thể sử dụng enum cụ thể này như nó được không? Làm cách nào để thực hiện 3 điều được liệt kê ở trên?


1
Như đã đề cập bên dưới, bạn có thể sử dụng một enum Flags. Bạn có thể muốn xem Phần trong và ngoài của C # enums , chứa nhiều thông tin hơn về cách làm việc với Flags enums.
ChaseMedallion

Câu trả lời:


181

Khi bạn xác định enum, chỉ cần gán nó bằng [Flags], đặt giá trị thành lũy thừa của hai và nó sẽ hoạt động theo cách này.

Không có gì khác thay đổi, ngoài việc chuyển nhiều giá trị vào một hàm.

Ví dụ:

[Flags]
enum DaysOfWeek
{
   Sunday = 1,
   Monday = 2,
   Tuesday = 4,
   Wednesday = 8,
   Thursday = 16,
   Friday = 32,
   Saturday = 64
}

public void RunOnDays(DaysOfWeek days)
{
   bool isTuesdaySet = (days & DaysOfWeek.Tuesday) == DaysOfWeek.Tuesday;

   if (isTuesdaySet)
      //...
   // Do your work here..
}

public void CallMethodWithTuesdayAndThursday()
{
    this.RunOnDays(DaysOfWeek.Tuesday | DaysOfWeek.Thursday);
}

Để biết thêm chi tiết, hãy xem tài liệu của MSDN về Các kiểu kê khai .


Chỉnh sửa để trả lời các bổ sung cho câu hỏi.

Bạn sẽ không thể sử dụng enum đó như hiện tại, trừ khi bạn muốn làm điều gì đó như chuyển nó dưới dạng mảng array / collection / params. Điều đó sẽ cho phép bạn chuyển nhiều giá trị. Cú pháp flags yêu cầu Enum phải được chỉ định dưới dạng cờ (hoặc làm sai lệch ngôn ngữ theo cách không được thiết kế).


9
Tôi nghĩ nếu bạn muốn điều này hoạt động, các giá trị cần được xác định là lũy thừa của hai, một bit duy nhất cho mỗi giá trị. Điều gì xảy ra là bạn chuyển OR theo chiều bit của các giá trị trong tham số, mà bạn có thể kiểm tra bằng cách sử dụng các phép toán bitwise AND. Nếu bạn không quan tâm đến việc xác định các giá trị, chẳng hạn như 1,2,4,8, v.v., bạn sẽ gặp vấn đề.
Denis Troller

1
Chỉ đặt thuộc tính Flags không tự động hoạt động. Tôi chỉ thử nó.
Richard Anthony Hein

1
Bạn nói đúng, và tôi đã xóa thông tin sai lệch. Đã sửa mã của Reed cho anh ta.
Matthew Scharley

@monoxide: Tôi đã sửa nó cùng lúc - xin lỗi về việc ghi đè các chỉnh sửa của bạn.
Reed Copsey

2
Không sao ... Tôi vẫn thích sử dụng hex cho các giá trị cờ như vậy, có vẻ rõ ràng hơn đối với tôi, nhưng mỗi giá trị của riêng anh ấy.
Matthew Scharley

78

Tôi nghĩ rằng giải pháp thanh lịch hơn là sử dụng HasFlag ():

    [Flags]
    public enum DaysOfWeek
    {
        Sunday = 1,
        Monday = 2,
        Tuesday = 4,
        Wednesday = 8,
        Thursday = 16,
        Friday = 32,
        Saturday = 64
    }

    public void RunOnDays(DaysOfWeek days)
    {
        bool isTuesdaySet = days.HasFlag(DaysOfWeek.Tuesday);

        if (isTuesdaySet)
        {
            //...
        }
    }

    public void CallMethodWithTuesdayAndThursday()
    {
        RunOnDays(DaysOfWeek.Tuesday | DaysOfWeek.Thursday);
    }

Có một điều quan trọng khác cần biết, nếu bạn làm theo cách này: Nó thêm tính linh hoạt hơn nhiều khi bạn cần điền vào enum động. Ví dụ: Nếu bạn sử dụng hộp kiểm để xác định ngày mà bạn muốn chương trình của mình chạy và bạn cố gắng thực hiện điều này theo cách đã thực hiện ở trên, thì mã sẽ không thể đọc được. Vì vậy, thay vào đó bạn có thể làm theo cách này: ... DaysOfWeek days = new DaysOfWeek(); if (cbTuesday.Checked) days |= DaysOfWeek.Tuesday; if (cbWednesday.Checked) days |= DaysOfWeek.Wednesday;...
CoastN

25

Tôi thứ hai câu trả lời của Reed. Tuy nhiên, khi tạo enum, bạn phải chỉ định các giá trị cho từng thành viên enum để nó tạo ra một trường bit. Ví dụ:

[Flags]
public enum DaysOfWeek
{
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64,

    None = 0,
    All = Weekdays | Weekend,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday,
    Weekend = Sunday | Saturday,
    // etc.
}

1
Nó sẽ hữu ích cho bạn nếu bạn cần kiểm tra giá trị DaysOfWeek, chẳng hạn như nếu bạn đang đọc một giá trị được lưu trữ trong cơ sở dữ liệu. Ngoài ra, nó cung cấp tài liệu tốt hơn cho các lập trình viên khác, vì một số ngày trong tuần bắt đầu từ Thứ Hai thay vì Chủ Nhật.
Jacob

3
Để rõ ràng và dễ bảo trì, tôi sẽ viết enum 'Tất cả' của bạn là "Chủ nhật | Thứ hai | Thứ ba | Thứ tư | Thứ năm | Thứ sáu | Thứ bảy" ... và sử dụng ký hiệu tương tự cho 'Các ngày trong tuần' và 'Cuối tuần'.
Robert Cartaino

Điều này là tốt nếu bạn muốn tạo enum riêng của bạn, nhưng để trả lời câu hỏi mà bạn không thể thay đổi enum DaysOfWeek hiện
Robert Paulson

2
Không phải "có thể muốn" ... bạn PHẢI. So sánh bitwise yêu cầu nó. Để lại các giá trị không xác định sẽ không hoạt động.
Richard Anthony Hein

11

Trong tình huống cụ thể của tôi, tôi muốn sử dụng System.DayOfWeek

Bạn không thể sử dụng System.DayOfWeek làm [Flags]bảng liệt kê vì bạn không có quyền kiểm soát nó. Nếu bạn muốn có một phương thức chấp nhận nhiều DayOfWeekthì bạn sẽ phải sử dụng paramstừ khóa

void SetDays(params DayOfWeek[] daysToSet)
{
    if (daysToSet == null || !daysToSet.Any())
        throw new ArgumentNullException("daysToSet");

    foreach (DayOfWeek day in daysToSet)
    {
        // if( day == DayOfWeek.Monday ) etc ....
    }
}

SetDays( DayOfWeek.Monday, DayOfWeek.Sunday );

Nếu không, bạn có thể tạo [Flags]bảng liệt kê của riêng mình như được phác thảo bởi nhiều người trả lời khác và sử dụng phép so sánh theo bit.


2
Tôi không biết tại sao tôi không nghĩ đến các thông số. Điều đó sẽ rất hữu ích cho việc sử dụng enum không phải là trường bit.
Ronnie Overby

10
[Flags]
public enum DaysOfWeek
{
  Mon = 1,
  Tue = 2,
  Wed = 4,
  Thur = 8,
  Fri = 16,
  Sat = 32,
  Sun = 64
}

Bạn phải chỉ định các số và tăng chúng như thế này bởi vì nó đang lưu trữ các giá trị theo kiểu bitwise.

Sau đó, chỉ cần xác định phương thức của bạn để lấy enum này

public void DoSomething(DaysOfWeek day)
{
  ...
}

và để gọi nó làm một cái gì đó như

DoSomething(DaysOfWeek.Mon | DaysOfWeek.Tue) // Both Monday and Tuesday

Để kiểm tra xem một trong các giá trị enum đã được bao gồm hay chưa, hãy kiểm tra chúng bằng cách sử dụng các phép toán bitwise như

public void DoSomething(DaysOfWeek day)
{
  if ((day & DaysOfWeek.Mon) == DaysOfWeek.Mon) // Does a bitwise and then compares it to Mondays enum value
  {
    // Monday was passed in
  }
}

Không trả lời câu hỏi. "Trong tình hình cụ thể của tôi, tôi muốn sử dụng các System.DayOfWeek"
Robert Paulson

Nhưng nó chính xác là những gì tôi đang tìm kiếm. Vì vậy, cảm ơn anyway.
Tillito

3
[Flags]
    public enum DaysOfWeek{


        Sunday = 1 << 0,
        Monday = 1 << 1,
        Tuesday = 1 << 2,
        Wednesday = 1 << 3,
        Thursday = 1 << 4,
        Friday =  1 << 5,
        Saturday =  1 << 6
    }

gọi phương thức ở định dạng này

MethodName (DaysOfWeek.Tuesday | DaysOfWeek.Thursday);

Triển khai phương thức EnumToArray để chuyển các tùy chọn

private static void AddEntryToList(DaysOfWeek days, DaysOfWeek match, List<string> dayList, string entryText) {
            if ((days& match) != 0) {
                dayList.Add(entryText);
            }
        }

        internal static string[] EnumToArray(DaysOfWeek days) {
            List<string> verbList = new List<string>();

            AddEntryToList(days, HttpVerbs.Sunday, dayList, "Sunday");
            AddEntryToList(days, HttpVerbs.Monday , dayList, "Monday ");
            ...

            return dayList.ToArray();
        }

Không trả lời câu hỏi. "Trong tình hình cụ thể của tôi, tôi muốn sử dụng các System.DayOfWeek"
Robert Paulson

Cảm ơn Robert, nhưng bạn thực sự không cần phải chỉ ra điều này trong mọi câu trả lời.
Ronnie Overby

@Ronnie - Lỗi của tôi là có quá nhiều câu trả lời gần như trùng lặp nhưng không thực sự trả lời câu hỏi.
Robert Paulson

@Rony - các enums ToString () và Enum.Parse () sẽ chính xác sản lượng / phân tích một [Flags] liệt kê (miễn là bạn đang cẩn thận liệt kê versioning tất nhiên)
Robert Paulson

@Robert - Tôi không nghĩ có ai đang đổ lỗi cho bạn. Hãy để những lá phiếu nói chuyện.
Ronnie Overby

3

Đánh dấu enum của bạn bằng thuộc tính [Flags]. Đồng thời đảm bảo rằng tất cả các giá trị của bạn là loại trừ lẫn nhau (hai giá trị không thể cộng lại bằng một giá trị khác) như 1,2,4,8,16,32,64 trong trường hợp của bạn

[Flags]
public enum DayOfWeek
{ 
Sunday = 1,   
Monday = 2,   
Tuesday = 4,   
Wednesday = 8,   
Thursday = 16,   
Friday = 32,    
Saturday = 64
}

Khi bạn có một phương thức chấp nhận một enum DayOfWeek, hãy sử dụng bitwise hoặc toán tử (|) để sử dụng nhiều thành viên cùng nhau. Ví dụ:

MyMethod(DayOfWeek.Sunday|DayOfWeek.Tuesday|DayOfWeek.Friday)

Để kiểm tra xem tham số có chứa một thành viên cụ thể hay không, hãy sử dụng bitwise toán tử (&) với thành viên bạn đang kiểm tra.

if(arg & DayOfWeek.Sunday == DayOfWeek.Sunday)
Console.WriteLine("Contains Sunday");

Không trả lời câu hỏi. "Trong tình hình cụ thể của tôi, tôi muốn sử dụng các System.DayOfWeek"
Robert Paulson

2

Reed Copsey đúng và tôi sẽ thêm vào bài đăng gốc nếu có thể, nhưng tôi không thể nên thay vào đó tôi sẽ phải trả lời.

Thật nguy hiểm nếu chỉ sử dụng [Cờ] trên bất kỳ enum cũ nào. Tôi tin rằng bạn phải thay đổi rõ ràng các giá trị enum thành lũy thừa của hai khi sử dụng cờ, để tránh xung đột trong các giá trị. Xem hướng dẫn về FlagsAttribute và Enum .



-3

Một cái gì đó có tính chất này sẽ hiển thị những gì bạn đang tìm kiếm:

[Flags]
public enum SomeName
{
    Name1,
    Name2
}

public class SomeClass()
{
    public void SomeMethod(SomeName enumInput)
    {
        ...
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.