2009-02-23 12 views
27

Nếu tôi có enum sau:Tôi có thể tránh truyền một giá trị enum khi cố gắng sử dụng hoặc trả lại giá trị đó không?

public enum ReturnValue{ 
    Success = 0, 
    FailReason1 = 1, 
    FailReason2 = 2 
    //Etc... 
} 

Tôi có thể tránh đúc khi tôi trở về, như thế này:

public static int main(string[] args){ 
    return (int)ReturnValue.Success; 
} 

Nếu không, tại sao không phải là một giá trị enum coi là một int theo mặc định ?

Trả lời

46

enums được cho là an toàn. Tôi nghĩ rằng họ đã không làm cho họ ngầm có thể castable để ngăn cản sử dụng khác. Mặc dù khung công tác cho phép bạn gán một giá trị không đổi cho chúng, bạn nên xem xét lại ý định của mình. Nếu bạn chủ yếu sử dụng enum để lưu trữ các giá trị không đổi, hãy xem xét sử dụng lớp tĩnh:

public static class ReturnValue 
{ 
    public const int Success = 0; 
    public const int FailReason1 = 1; 
    public const int FailReason2 = 2; 
    //Etc... 
} 

Điều đó cho phép bạn thực hiện việc này.

public static int main(string[] args){ 
    return ReturnValue.Success; 
} 

EDIT

Khi bạn làm muốn cung cấp giá trị cho một enum là khi bạn muốn kết hợp chúng. Xem ví dụ bên dưới:

[Flags] // indicates bitwise operations occur on this enum 
public enum DaysOfWeek : byte // byte type to limit size 
{ 
    Sunday = 1, 
    Monday = 2, 
    Tuesday = 4, 
    Wednesday = 8, 
    Thursday = 16, 
    Friday = 32, 
    Saturday = 64, 
    Weekend = Sunday | Saturday, 
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday 
} 

Enum này sau đó có thể được tiêu thụ bằng cách sử dụng toán học bitwise. Xem ví dụ dưới đây cho một số ứng dụng.

public static class DaysOfWeekEvaluator 
{ 
    public static bool IsWeekends(DaysOfWeek days) 
    { 
     return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend; 
    } 

    public static bool IsAllWeekdays(DaysOfWeek days) 
    { 
     return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays; 
    } 

    public static bool HasWeekdays(DaysOfWeek days) 
    { 
     return ((int) (days & DaysOfWeek.Weekdays)) > 0; 
    } 

    public static bool HasWeekendDays(DaysOfWeek days) 
    { 
     return ((int) (days & DaysOfWeek.Weekend)) > 0; 
    } 
} 
+0

Làm việc như một sự quyến rũ, Cảm ơn! Lạ lùng là bạn có thể định nghĩa một enum: "public enum MyEnum: int {...}" và không có giá trị của nó ngầm có thể cast. Không có khái niệm về một "enum chung" là có? - công khai enum MyEnum - hoặc là điều này hoàn toàn vô lý? – Pwninstein

+0

Bạn có thể delcare gõ vào enum của bạn "enum công cộng ReturnValue: int" mà thi hành rằng các giá trị là loại int. Nó vẫn không cung cấp khả năng truyền ngầm. Chỉnh sửa để hiển thị khi nó _is_ một ý tưởng tốt để sử dụng các giá trị trong một enum. –

+2

Một gợi ý nhỏ - sử dụng 'static readonly int' thay vì' const', trong trường hợp giá trị của bạn thay đổi trong các phiên bản sau. Bạn không muốn người gọi có giá trị đó được biên dịch vào mã của họ. –

2

Enums và ints chỉ đơn giản là không thể castable theo spec (ngoại trừ chữ 0, được phép kiểm tra so sánh/gán/etc). Tuy nhiên, dàn diễn viên rõ ràng là tất cả những gì cần thiết.

+2

(spec 13.1.3 và 13.2.2 của ECMA 334v4) –

+0

Nó muốn được tốt đẹp nếu bạn có thể ghi đè lên các nhà điều hành bình đẳng cho enums –

+0

@ Chris bạn có thể viết một phương pháp khuyến nông , có lẽ. Không hoàn toàn giống nhau, nhưng ... –

0

Không, bạn không thể tránh truyền; tại sao không có chuyển đổi tiềm ẩn, tôi không biết, nhưng không có.

4

Không có dàn diễn viên tiềm ẩn vì enum không phải sử dụng int làm loại cơ bản. Nếu enum của bạn sử dụng một uint như kiểu cơ bản, ví dụ, không có diễn viên tiềm ẩn từ uint đến int.

+1

Hai upvotes? Tôi thấy câu trả lời này không thuận lợi ở chỗ nó trả lời câu hỏi được viết, nhưng không phải là câu hỏi rõ ràng đang được hỏi. Nếu bạn định nghĩa 'enum MyEnum: int {}' ?; sau đó nó không phải, nhưng hiệu quả sử dụng int như kiểu cơ bản; và vẫn không có quá trình truyền ngầm. sadpanda :( – ANeves

0

Kỳ lạ, điều này không cụ thể đối với Khuôn khổ .NET, nhưng chỉ với C#. Như những người bình luận khác đã chỉ ra, trong C# điều này về cơ bản là một đặc tả của ngôn ngữ. Điều này cũng không đúng trong VB.NET.

Kiểm tra trang tham chiếu MSDN cho Enums in VB.NET. Lưu ý rằng bạn có thể chỉ định kiểu dữ liệu của một liệt kê tại thời gian khai báo Enum.

Điều đó có nghĩa, nếu bạn thực sự không muốn xả mã của bạn bằng phôi đến (int), bạn có thể viết liệt kê trong VB.NET, khai báo nó làm số nguyên, sau đó sử dụng Enum đó từ C#.

Hãy nhớ cách họ nói với chúng tôi rằng máy tính sẽ làm cho cuộc sống của chúng ta đơn giản hơn nhiều? :)

+0

Bạn sẽ gặp phải vấn đề tương tự ở đây, vì bạn đang tiêu thụ enum trong C#, và hãy nhớ, C# không thể chuyển đổi hoàn toàn enum! Bạn đang quay lại hình vuông;) –

0

Bạn có thể gán hành vi này theo ý định cơ bản đằng sau việc tạo Danh sách ... để tạo tập hợp các hằng số có tên chỉ có thể có giá trị được chỉ định (hoặc mặc định) tùy thuộc vào loại cơ bản.

Có hai vấn đề riêng biệt để xem xét, như liên quan đến câu hỏi của bạn:

  1. Một giá trị Enum không thể được coi là một int theo mặc định vì sau đó bạn sẽ có thể cung cấp bất kỳ số nguyên và có sẽ không có kiểm tra thời gian biên dịch để xác nhận rằng số nguyên được cung cấp thực tế tồn tại dưới dạng một giá trị trong Liệt kê.

  2. Đúc trở nên cần thiết kể từ khi bạn đang cố gắng để chuyển đổi từ kiểu quản (loại YourCustomEnum mà xuất phát từ lớp System.Enum) để loại cơ bản, ví dụ, int hay byte vv

4

Các C# enum là vô ích.

Bạn có thể tránh truyền từ loại của bạn VÀ hạn chế các giá trị có thể được đúc một cách rõ ràng với loại của bạn bằng cách tạo lớp kín và cung cấp toán tử chuyển đổi ngầm/rõ ràng.

  • Cung cấp toán tử ngầm để chuyển đổi từ loại của bạn thành int chung để bạn không phải truyền.
  • Cung cấp toán tử rõ ràng để chuyển đổi từ int sang loại của bạn, lỗi này nếu số nguyên không đáp ứng được ràng buộc, chẳng hạn như (int x) => (x> = 0 & & x < = 2).

Nếu sử dụng kỹ thuật này, tạo ra một generic bất biến lớp cơ sở như ConstrainedNumber<T>, trong đó có một nhà xây dựng mà chấp nhận một giá trị T và đại biểu cho sự hạn chế: delegate bool NumberConstraint<T>(T value). Hàm khởi tạo nên chạy giá trị thông qua ủy nhiệm hạn chế và ném một ngoại lệ nếu nó không đáp ứng được ràng buộc. Lớp cơ sở cũng nên xử lý hoạt động chuyển đổi ngầm định thành T và cần xử lý bình đẳng bằng cách nạp chồng đối tượng.Equals (object) và object.GetHashCode(), định nghĩa == và! = Toán tử cho loại ConstrainedNumber<T> và triển khai IEquatable<T>IEquatable<ConstrainedNumber<T>>. Tôi cũng khuyên bạn nên định nghĩa một hàm tạo bản sao cho lớp cơ sở và tất cả các kiểu có nguồn gốc. Nhân bản sau đó có thể được thực hiện một cách sạch sẽ trong lớp cơ sở bằng cách truy xuất hàm tạo bản sao thông qua sự phản chiếu, nhưng điều này hoàn toàn là tùy chọn. Bạn có thể tự mình thực hiện việc thực hiện ConstrainedNumber<T>, trừ khi tôi đã đăng nó lên stackoverflow ở đâu đó.

Bạn có thể cung cấp giá trị chỉ đọc tĩnh được đặt tên trong ConstrainedNumber bắt nguồn để bạn có thể truy cập chúng giống như một enum.

public sealed class ReturnValue: ConstrainedNumber<int> 
{ 
    public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3); 

    public static readonly ReturnValue Success = new ReturnValue(0); 
    public static readonly ReturnValue FailReason1 = new ReturnValue(1); 
    public static readonly ReturnValue FailReason2 = new ReturnValue(2); 

    private ReturnValue(int value): base(value, constraint) {} 
    private ReturnValue(ReturnValue original): base (original) {} //may be used to support IClonable implementation in base class 
    public static explicit operator ReturnValue(int value) 
    { 
     switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public. 
     { 
      case 0: return Success; 
      case 1: return FailReason1; 
      case 2: return FailReason2; 
     } 
     throw new ArgumentException("Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value"); 
    } 

} 

Bạn có thể sử dụng kỹ thuật này cho bất kỳ ràng buộc nào. Ví dụ, một lớp được gọi là EvenNumber có thể có một ràng buộc trả về true nếu số đã cho là chẵn. Trong trường hợp đó, bạn chỉ cần tạo các hàm khởi tạo của bạn, và đơn giản hóa toán tử chuyển đổi tĩnh của bạn để trả về một EvenNumber mới, thay vì chuyển đổi để trả về một trong các cá thể có giới hạn hiện có.

Nó có thể được sử dụng như thế này:

EvenNumber x = (EvenNumber)2; 
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber." A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model 
int z = x; //implicit conversion, no cast necessary; 
-2

Làm thế nào về việc sử dụng thành viên tĩnh của một lớp?

//enum DocInfos { DocName, DocNumber, DocVersion}; 
public class DocInfos 
{ 
    public static int DocName = 0; 
    public static int DocNumer = 1; 
    public static int DocVersion = 2; 
} 

...

  Doc = new string[DocInfos.DocVersion]; 
      // Treffer 
      Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2]) 

...

+0

Chỉ cần thông tin - Tôi đã không bỏ phiếu này xuống, nhưng tôi đặt cược nó đã được bình chọn xuống b/c đó là cả hai dư thừa của câu trả lời bình chọn hàng đầu và không chính xác. Lớp DocInfos phải tĩnh. –

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