2008-08-10 28 views
79

Những gì tôi muốn làm là một cái gì đó như thế này: Tôi có enums với các giá trị được gắn cờ kết hợp.Bất cứ ai cũng biết cách giải quyết tốt cho việc thiếu một ràng buộc chung enum?

public static class EnumExtension 
{ 
    public static bool IsSet<T>(this T input, T matchTo) 
     where T:enum //the constraint I want that doesn't exist in C#3 
    {  
     return (input & matchTo) != 0; 
    } 
} 

Vì vậy, sau đó tôi có thể làm:

MyEnum tester = MyEnum.FlagA | MyEnum.FlagB 

if(tester.IsSet(MyEnum.FlagA)) 
    //act on flag a 

Đáng tiếc là C# 's generic nơi khó khăn không có hạn chế enum, chỉ có lớp và struct. C# không thấy enums là cấu trúc (mặc dù chúng là các kiểu giá trị) vì vậy tôi không thể thêm các kiểu mở rộng như thế này.

Bất kỳ ai biết cách giải quyết?

+2

Keith: tải về phiên bản 0.0.0.2 của UnconstrainedMelody - Tôi đã thực hiện HasAll và HasAny. Thưởng thức. –

+0

Ý của bạn là gì bởi “C# không thấy enums là cấu trúc”? Bạn có thể sử dụng các loại enum như các tham số kiểu bị ràng buộc với 'struct' tốt. – Timwi

+0

kiểm tra bài viết này ở đây: http://www.codeproject.com/KB/cs/ExtendEnum.aspx 'IsValidEnumValue' hoặc các phương pháp 'IsFlagsEnumDefined' có lẽ là câu trả lời cho câu hỏi của bạn. – dmihailescu

Trả lời

46

CHỈNH SỬA: Điều này hiện đang được phát hành trong phiên bản 0.0.0.2 của UnconstrainedMelody.

(Theo yêu cầu trên blog post about enum constraints của tôi. Tôi đã bao gồm các sự kiện cơ bản dưới đây vì lợi ích của một câu trả lời độc lập.)

Giải pháp tốt nhất là để chờ đợi cho tôi để bao gồm nó trong UnconstrainedMelody . Đây là một thư viện mà mất mã C# với các ràng buộc "giả" như

where T : struct, IEnumConstraint 

và biến nó thành

where T : struct, System.Enum 

qua một bước postbuild.

Nó không quá khó để viết IsSet ... mặc dù phục vụ cho cả hai cờ Int64 dựa trên và UInt64 dựa trên có thể là một phần khó khăn. (Tôi ngửi thấy một số phương pháp helper tới trên, về cơ bản cho phép tôi để điều trị bất kỳ cờ enum như thể nó đã có một kiểu cơ sở của UInt64.)

Những gì bạn muốn hành vi là nếu bạn gọi

tester.IsSet(MyFlags.A | MyFlags.C) 

? Có nên kiểm tra xem tất cả cờ được chỉ định có được đặt không? Đó sẽ là kỳ vọng của tôi.

Tôi sẽ cố gắng làm điều này trên đường về nhà tối nay ... Tôi hy vọng sẽ có một blitz nhanh chóng về phương pháp enum hữu ích để có được thư viện lên đến một tiêu chuẩn có thể sử dụng một cách nhanh chóng, sau đó thư giãn một chút.

CHỈNH SỬA: Tôi không chắc chắn về tên IsSet như một cái tên.Tùy chọn:

  • Bao gồm
  • HasFlag (hoặc HasFlags)
  • isset (nó chắc chắn là một lựa chọn)

Suy nghĩ hoan nghênh. Tôi chắc chắn rằng nó sẽ là một thời gian trước khi đặt bất cứ điều gì trong đá anyway ...


hoặc trình nó như một miếng vá, tất nhiên ...

+0

Tôi cho rằng nếu nhiều cờ được thông qua trong đó nên kiểm tra cho tất cả chúng. Sửa chữa thực tế của tôi cho điều này (trở lại trong năm 2008 khi tôi hỏi nó) đã có một phương pháp mở rộng mẫu cho mỗi lá cờ enum - lộn xộn nhưng hoạt động. Không bao giờ bận tâm với việc kiểm tra nhiều cờ vì tất cả các kiểm tra chúng tôi có cho một lá cờ - không phải vấn đề như vậy trong mã chỉ nội bộ mà là một thứ cần phải được tính trong một thư viện được chia sẻ. – Keith

+1

Bạn phải đi và đề cập đến PostSharp LOL: o http://www.postsharp.org/blog/generic-constraints-for-enums-and-delegates –

+0

tôi sẽ sử dụng Flags-thuật ngữ đơn giản chỉ vì nó đã tồn tại trong .NET (xem 'FlagsAttribute'.) Tôi thấy hai tên rõ ràng ở đây:' HasAnyFlag' và 'HasAllFlags'. Chúng có thể được rút ngắn thành 'HasFlag' và' HasFlags'. Tôi không thể nói đó là tốt nhất, đó là một vấn đề của hương vị tôi đoán. – Blixt

16

Darren, điều đó sẽ có tác dụng nếu các loại là kiểu liệt kê cụ thể - cho enumerations chung để làm việc bạn phải bỏ họ ints (hoặc nhiều khả năng uint) để làm toán boolean:

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

Và nếu bạn có một số vô lý của cờ, bạn có thể gọi GetTypeCode() trên các đối số và Convert.ToUint64() – Kit

+0

Awesome, sự kết hợp của 'Enum' và' Convert.ToUInt32' tôi không tìm thấy bất cứ nơi nào khác. AFAIK, đó là giải pháp Pre-Net-4 phong nha duy nhất cũng hoạt động trong VB. BTW, nếu 'matchTo' có thể có nhiều bit cờ, sau đó thay thế'! = 0' bằng '== Convert.ToUInt32 (matchTo)'. – ToolmakerSteve

+1

Lưu ý rằng 'Convert.ToUInt32' được sử dụng với một enum sẽ sử dụng' Convert.ToUInt32 (đối tượng) 'overload, có nghĩa là CLR sẽ đầu tiên hộp các giá trị này trước khi chuyển đến phương thức' ToUInt32'. Trong hầu hết các trường hợp, điều này sẽ không thành vấn đề, nhưng bạn nên biết rằng bạn sẽ giữ cho GC khá bận rộn nếu bạn đang sử dụng một cái gì đó như thế này để phân tích hàng triệu enums mỗi giây. – Groo

1

Sử dụng ban đầu của bạn mã, bên trong phương pháp nào bạn cũng có thể sử dụng phản ánh để kiểm tra T đó là một enum:

public static class EnumExtension 
{ 
    public static bool IsSet<T>(this T input, T matchTo) 
    { 
     if (!typeof(T).IsEnum) 
     { 
      throw new ArgumentException("Must be an enum", "input"); 
     } 
     return (input & matchTo) != 0; 
    } 
} 
+2

Cảm ơn, nhưng điều đó biến một vấn đề thời gian biên dịch (nơi hạn chế) thành một thời gian chạy (ngoại lệ của bạn). Ngoài ra, bạn vẫn cần phải chuyển đổi các đầu vào thành int trước khi bạn có thể làm bất kỳ điều gì với chúng. – Keith

3

cách tôi làm điều đó là đặt một hạn chế struct, sau đó kiểm tra xem T là một enum khi chạy. Điều này không loại trừ hoàn toàn vấn đề, nhưng nó làm giảm nó một chút

+7

nơi T: struct, IComparable, IFormattable, IConvertible - đây là gần nhất bạn có thể tới enum :) – Kit

9

Trên thực tế, nó có thể , với một thủ thuật xấu xí. Tuy nhiên, nó không thể được sử dụng cho các phương pháp mở rộng.

public abstract class Enums<Temp> where Temp : class { 
    public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp { 
     return (TEnum)Enum.Parse(typeof(TEnum), name); 
    } 
} 
public abstract class Enums : Enums<Enum> { } 

Enums.IsSet<DateTimeKind>("Local") 

Nếu bạn muốn, bạn có thể cho Enums<Temp> một constructor riêng và lồng lớp được thừa kế trừu tượng nào với Temp như Enum, để ngăn chặn các phiên bản kế thừa cho người không đếm.

1

Dưới đây là một số mã mà tôi vừa làm mà dường như hoạt động như bạn muốn mà không phải làm bất cứ điều gì quá điên rồ. Nó không bị giới hạn chỉ những enums được đặt làm Cờ, nhưng luôn luôn có thể là một kiểm tra đưa vào nếu cần thiết.

public static class EnumExtensions 
{ 
    public static bool ContainsFlag(this Enum source, Enum flag) 
    { 
     var sourceValue = ToUInt64(source); 
     var flagValue = ToUInt64(flag); 

     return (sourceValue & flagValue) == flagValue; 
    } 

    public static bool ContainsAnyFlag(this Enum source, params Enum[] flags) 
    { 
     var sourceValue = ToUInt64(source); 

     foreach (var flag in flags) 
     { 
      var flagValue = ToUInt64(flag); 

      if ((sourceValue & flagValue) == flagValue) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 

    // found in the Enum class as an internal method 
    private static ulong ToUInt64(object value) 
    { 
     switch (Convert.GetTypeCode(value)) 
     { 
      case TypeCode.SByte: 
      case TypeCode.Int16: 
      case TypeCode.Int32: 
      case TypeCode.Int64: 
       return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture); 

      case TypeCode.Byte: 
      case TypeCode.UInt16: 
      case TypeCode.UInt32: 
      case TypeCode.UInt64: 
       return Convert.ToUInt64(value, CultureInfo.InvariantCulture); 
     } 

     throw new InvalidOperationException("Unknown enum type."); 
    } 
} 
4

này không trả lời câu hỏi ban đầu, nhưng bây giờ có một phương pháp trong .NET 4 gọi Enum.HasFlag mà làm những gì bạn đang cố gắng làm trong ví dụ của bạn

+0

Được thăng hạng bởi vì tại thời điểm này, hầu hết mọi người nên sử dụng .NET 4 (hoặc cao hơn) và vì vậy họ nên sử dụng phương pháp này thay vì cố gắng để hack nó lại với nhau. – CptRobby

+0

Được thăng hạng. Tuy nhiên giải pháp của họ sử dụng boxing của đối số 'flag'. .NET 4.0 đã được 5 tuổi rồi. –

8

Bạn có thể đạt được điều này bằng IL Dệt và ExtraConstraints

cho phép bạn viết mã này

public class Sample 
{ 
    public void MethodWithDelegateConstraint<[DelegateConstraint] T>() 
    {   
    } 
    public void MethodWithEnumConstraint<[EnumConstraint] T>() 
    { 
    } 
} 

Những gì được biên soạn

public class Sample 
{ 
    public void MethodWithDelegateConstraint<T>() where T: Delegate 
    { 
    } 

    public void MethodWithEnumConstraint<T>() where T: struct, Enum 
    { 
    } 
} 
0

Tôi chỉ muốn thêm Enum làm ràng buộc chung.

Trong khi đây chỉ là một phương pháp trợ giúp nhỏ sử dụng ExtraConstraints là một chút quá nhiều chi phí cho tôi.

Tôi quyết định chỉ cần tạo một ràng buộc struct và thêm kiểm tra thời gian chạy cho IsEnum. Để chuyển đổi một biến từ T thành Enum, tôi chuyển nó thành đối tượng đầu tiên.

public static Converter<T, string> CreateConverter<T>() where T : struct 
    { 
     if (!typeof(T).IsEnum) throw new ArgumentException("Given Type is not an Enum"); 
     return new Converter<T, string>(x => ((Enum)(object)x).GetEnumDescription()); 
    } 
0

nếu ai đó cần generic isset (tạo ra các hộp trên ruồi có thể được cải thiện trên), và hoặc chuỗi Enum chuyển đổi onfly (trong đó sử dụng EnumConstraint trình bày dưới đây):

public class TestClass 
    { } 

    public struct TestStruct 
    { } 

    public enum TestEnum 
    { 
    e1,  
    e2, 
    e3 
    } 

    public static class TestEnumConstraintExtenssion 
    { 

    public static bool IsSet<TEnum>(this TEnum _this, TEnum flag) 
     where TEnum : struct 
    { 
     return (((uint)Convert.ChangeType(_this, typeof(uint))) & ((uint)Convert.ChangeType(flag, typeof(uint)))) == ((uint)Convert.ChangeType(flag, typeof(uint))); 
    } 

    //public static TestClass ToTestClass(this string _this) 
    //{ 
    // // #generates compile error (so no missuse) 
    // return EnumConstraint.TryParse<TestClass>(_this); 
    //} 

    //public static TestStruct ToTestStruct(this string _this) 
    //{ 
    // // #generates compile error (so no missuse) 
    // return EnumConstraint.TryParse<TestStruct>(_this); 
    //} 

    public static TestEnum ToTestEnum(this string _this) 
    { 
     // #enum type works just fine (coding constraint to Enum type) 
     return EnumConstraint.TryParse<TestEnum>(_this); 
    } 

    public static void TestAll() 
    { 
     TestEnum t1 = "e3".ToTestEnum(); 
     TestEnum t2 = "e2".ToTestEnum(); 
     TestEnum t3 = "non existing".ToTestEnum(); // default(TestEnum) for non existing 

     bool b1 = t3.IsSet(TestEnum.e1); // you can ommit type 
     bool b2 = t3.IsSet<TestEnum>(TestEnum.e2); // you can specify explicite type 

     TestStruct t; 
     // #generates compile error (so no missuse) 
     //bool b3 = t.IsSet<TestEnum>(TestEnum.e1); 

    } 

    } 

Nếu ai đó vẫn cần ví dụ nóng để tạo ràng buộc mã hóa Enum:

using System; 

/// <summary> 
/// would be same as EnumConstraint_T&lt;Enum>Parse&lt;EnumType>("Normal"), 
/// but writen like this it abuses constrain inheritence on System.Enum. 
/// </summary> 
public class EnumConstraint : EnumConstraint_T<Enum> 
{ 

} 

/// <summary> 
/// provides ability to constrain TEnum to System.Enum abusing constrain inheritence 
/// </summary> 
/// <typeparam name="TClass">should be System.Enum</typeparam> 
public abstract class EnumConstraint_T<TClass> 
    where TClass : class 
{ 

    public static TEnum Parse<TEnum>(string value) 
    where TEnum : TClass 
    { 
    return (TEnum)Enum.Parse(typeof(TEnum), value); 
    } 

    public static bool TryParse<TEnum>(string value, out TEnum evalue) 
    where TEnum : struct, TClass // struct is required to ignore non nullable type error 
    { 
    evalue = default(TEnum); 
    return Enum.TryParse<TEnum>(value, out evalue); 
    } 

    public static TEnum TryParse<TEnum>(string value, TEnum defaultValue = default(TEnum)) 
    where TEnum : struct, TClass // struct is required to ignore non nullable type error 
    {  
    Enum.TryParse<TEnum>(value, out defaultValue); 
    return defaultValue; 
    } 

    public static TEnum Parse<TEnum>(string value, TEnum defaultValue = default(TEnum)) 
    where TEnum : struct, TClass // struct is required to ignore non nullable type error 
    { 
    TEnum result; 
    if (Enum.TryParse<TEnum>(value, out result)) 
     return result; 
    return defaultValue; 
    } 

    public static TEnum Parse<TEnum>(ushort value) 
    { 
    return (TEnum)(object)value; 
    } 

    public static sbyte to_i1<TEnum>(TEnum value) 
    { 
    return (sbyte)(object)Convert.ChangeType(value, typeof(sbyte)); 
    } 

    public static byte to_u1<TEnum>(TEnum value) 
    { 
    return (byte)(object)Convert.ChangeType(value, typeof(byte)); 
    } 

    public static short to_i2<TEnum>(TEnum value) 
    { 
    return (short)(object)Convert.ChangeType(value, typeof(short)); 
    } 

    public static ushort to_u2<TEnum>(TEnum value) 
    { 
    return (ushort)(object)Convert.ChangeType(value, typeof(ushort)); 
    } 

    public static int to_i4<TEnum>(TEnum value) 
    { 
    return (int)(object)Convert.ChangeType(value, typeof(int)); 
    } 

    public static uint to_u4<TEnum>(TEnum value) 
    { 
    return (uint)(object)Convert.ChangeType(value, typeof(uint)); 
    } 

} 

hy vọng điều này sẽ giúp ai đó.

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