it-swarm-vi.tech

Tạo phương thức Chung ràng buộc T thành Enum

Tôi đang xây dựng một hàm để mở rộng khái niệm Enum.Parse

  • Cho phép phân tích giá trị mặc định trong trường hợp không tìm thấy giá trị Enum
  • Là trường hợp không nhạy cảm

Vì vậy, tôi đã viết như sau:

public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
    if (string.IsNullOrEmpty(value)) return defaultValue;
    foreach (T item in Enum.GetValues(typeof(T)))
    {
        if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
    }
    return defaultValue;
}

Tôi đang nhận được một ràng buộc lỗi không thể là lớp đặc biệt System.Enum.

Đủ công bằng, nhưng có một cách giải quyết để cho phép một Enum chung, hoặc tôi sẽ phải bắt chước hàm Parse và chuyển một loại như một thuộc tính, điều này buộc yêu cầu quyền anh xấu xí vào mã của bạn.

CHỈNH SỬA Tất cả các đề xuất bên dưới đã được đánh giá rất cao, cảm ơn.

Đã giải quyết (Tôi đã rời khỏi vòng lặp để duy trì tính không nhạy cảm của trường hợp - Tôi đang sử dụng điều này khi phân tích cú pháp XML)

public static class EnumUtils
{
    public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
        if (string.IsNullOrEmpty(value)) return defaultValue;

        foreach (T item in Enum.GetValues(typeof(T)))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

EDIT: (ngày 16 tháng 2 năm 2015) Julien Lebosquain gần đây đã đăng một trình biên dịch thực thi giải pháp chung an toàn loại trong MSIL hoặc F # bên dưới, rất đáng giá nhìn, và một upvote. Tôi sẽ xóa chỉnh sửa này nếu giải pháp bong bóng hơn nữa trên trang.

1059
johnc

Enum Loại thực hiện giao diện IConvertible, nên việc triển khai tốt hơn sẽ giống như thế này:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

Điều này vẫn sẽ cho phép chuyển các loại giá trị triển khai IConvertible. Cơ hội là rất hiếm mặc dù.

935
Vivek

Tính năng này cuối cùng được hỗ trợ trong C # 7.3!

Đoạn mã sau (từ các mẫu dotnet ) trình bày cách:

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

Hãy chắc chắn đặt phiên bản ngôn ngữ của bạn trong dự án C # của bạn thành phiên bản 7.3.


Câu trả lời gốc dưới đây:

Tôi đến trễ trò chơi, nhưng tôi coi đó là một thử thách để xem làm thế nào nó có thể được thực hiện. Không thể có trong C # (hoặc VB.NET, nhưng cuộn xuống cho F #), nhưng là có thể trong MSIL. Tôi đã viết một chút .... điều này

// license: http://www.Apache.org/licenses/LICENSE-2.0.html
.Assembly MyThing{}
.class public abstract sealed MyThing.Thing
       extends [mscorlib]System.Object
{
  .method public static !!T  GetEnumFromString<valuetype .ctor ([mscorlib]System.Enum) T>(string strValue,
                                                                                          !!T defaultValue) cil managed
  {
    .maxstack  2
    .locals init ([0] !!T temp,
                  [1] !!T return_value,
                  [2] class [mscorlib]System.Collections.IEnumerator enumerator,
                  [3] class [mscorlib]System.IDisposable disposer)
    // if(string.IsNullOrEmpty(strValue)) return defaultValue;
    ldarg strValue
    call bool [mscorlib]System.String::IsNullOrEmpty(string)
    brfalse.s HASVALUE
    br RETURNDEF         // return default it empty

    // foreach (T item in Enum.GetValues(typeof(T)))
  HASVALUE:
    // Enum.GetValues.GetEnumerator()
    ldtoken !!T
    call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 
    stloc enumerator
    .try
    {
      CONDITION:
        ldloc enumerator
        callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        brfalse.s LEAVE

      STATEMENTS:
        // T item = (T)Enumerator.Current
        ldloc enumerator
        callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
        unbox.any !!T
        stloc temp
        ldloca.s temp
        constrained. !!T

        // if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        callvirt instance string [mscorlib]System.Object::ToString()
        callvirt instance string [mscorlib]System.String::ToLower()
        ldarg strValue
        callvirt instance string [mscorlib]System.String::Trim()
        callvirt instance string [mscorlib]System.String::ToLower()
        callvirt instance bool [mscorlib]System.String::Equals(string)
        brfalse.s CONDITION
        ldloc temp
        stloc return_value
        leave.s RETURNVAL

      LEAVE:
        leave.s RETURNDEF
    }
    finally
    {
        // ArrayList's Enumerator may or may not inherit from IDisposable
        ldloc enumerator
        isinst [mscorlib]System.IDisposable
        stloc.s disposer
        ldloc.s disposer
        ldnull
        ceq
        brtrue.s LEAVEFINALLY
        ldloc.s disposer
        callvirt instance void [mscorlib]System.IDisposable::Dispose()
      LEAVEFINALLY:
        endfinally
    }

  RETURNDEF:
    ldarg defaultValue
    stloc return_value

  RETURNVAL:
    ldloc return_value
    ret
  }
} 

Mà tạo ra một hàm mà sẽ trông như thế này, nếu nó hợp lệ C #:

T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum

Sau đó, với mã C # sau:

using MyThing;
// stuff...
private enum MyEnum { Yes, No, Okay }
static void Main(string[] args)
{
    Thing.GetEnumFromString("No", MyEnum.Yes); // returns MyEnum.No
    Thing.GetEnumFromString("Invalid", MyEnum.Okay);  // returns MyEnum.Okay
    Thing.GetEnumFromString("AnotherInvalid", 0); // compiler error, not an Enum
}

Thật không may, điều này có nghĩa là có phần mã này được viết bằng MSIL thay vì C #, với lợi ích duy nhất được thêm vào là bạn có thể hạn chế phương thức này bằng System.Enum. Nó cũng là một người lập dị, bởi vì nó được biên soạn thành một hội riêng. Tuy nhiên, điều đó không có nghĩa là bạn phải triển khai theo cách đó.

Bằng cách xóa dòng .Assembly MyThing{} và gọi ilasm như sau:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

bạn nhận được một netmodule thay vì một hội.

Thật không may, VS2010 (và trước đó, rõ ràng) không hỗ trợ thêm các tham chiếu netmodule, điều đó có nghĩa là bạn phải để nó thành 2 cụm riêng biệt khi bạn gỡ lỗi. Cách duy nhất bạn có thể thêm chúng như một phần của Hội đồng của mình là tự chạy csc.exe bằng cách sử dụng đối số dòng lệnh /addmodule:{files}. Sẽ không quá đau đớn trong tập lệnh MSBuild. Tất nhiên, nếu bạn dũng cảm hoặc ngu ngốc, bạn có thể tự chạy csc bằng tay mỗi lần. Và nó chắc chắn trở nên phức tạp hơn khi nhiều hội đồng cần truy cập vào nó.

Vì vậy, nó có thể được thực hiện trong .Net. Có đáng để nỗ lực thêm không? Ừm, tôi đoán tôi sẽ để bạn quyết định cái đó.


Giải pháp F # thay thế

Tín dụng bổ sung: Hóa ra hạn chế chung đối với enum có thể có ở ít nhất một ngôn ngữ .NET khác ngoài MSIL: F #.

type MyThing =
    static member GetEnumFromString<'T when 'T :> Enum> str defaultValue: 'T =
        /// protect for null (only required in interop with C#)
        let str = if isNull str then String.Empty else str

        Enum.GetValues(typedefof<'T>)
        |> Seq.cast<_>
        |> Seq.tryFind(fun v -> String.Compare(v.ToString(), str.Trim(), true) = 0)
        |> function Some x -> x | None -> defaultValue

Ngôn ngữ này dễ bảo trì hơn vì đây là ngôn ngữ nổi tiếng với sự hỗ trợ đầy đủ của Visual Studio IDE, nhưng bạn vẫn cần một dự án riêng trong giải pháp cho nó. Tuy nhiên, nó tự nhiên tạo ra IL khác nhau đáng kể (mã rất khác nhau) và nó dựa vào thư viện FSharp.Core, giống như mọi thư viện bên ngoài khác, cần phải trở thành một phần của bạn phân phối.

Đây là cách bạn có thể sử dụng nó (về cơ bản giống như giải pháp MSIL) và để chỉ ra rằng nó thất bại chính xác trên các cấu trúc đồng nghĩa khác:

// works, result is inferred to have type StringComparison
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", StringComparison.Ordinal);
// type restriction is recognized by C#, this fails at compile time
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", 42);
537
Christopher Currens

C # ≥ 7.3

Bắt đầu với C # 7.3 (có sẵn với Visual Studio 2017 ≥ v15.7), mã này hiện hoàn toàn hợp lệ:

public static TEnum Parse<TEnum>(string value)
where TEnum : struct, Enum { ... }

C # ≤ 7.2

Bạn có thể có một trình biên dịch thực sự thực thi ràng buộc enum bằng cách lạm dụng kế thừa ràng buộc. Đoạn mã sau chỉ định cả hai ràng buộc classstruct cùng một lúc:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

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

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

Sử dụng:

EnumUtils.Parse<SomeEnum>("value");

Lưu ý: điều này được quy định cụ thể trong đặc tả ngôn ngữ C # 5.0:

Nếu tham số loại S phụ thuộc vào tham số loại T thì: [...] S có giá trị ràng buộc loại giá trị và T có ràng buộc loại tham chiếu. Thực tế, điều này giới hạn T ở các loại System.Object, System.ValueType, System.Enum và bất kỳ loại giao diện nào.

186
Julien Lebosquain

Chỉnh sửa

Câu hỏi hiện đã được trả lời một cách tuyệt vời bởi Julien Lebosquain . Tôi cũng muốn mở rộng câu trả lời của anh ấy bằng ignoreCasename__, defaultValuevà các đối số tùy chọn, đồng thời thêm TryParseParseOrDefaultname__.

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

Ví dụ về cách sử dụng:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

Những cải tiến cũ của tôi về câu trả lời của Vivek bằng cách sử dụng các nhận xét và phát triển 'mới':

  • sử dụng TEnumđể rõ ràng cho người dùng
  • thêm nhiều ràng buộc giao diện để kiểm tra ràng buộc bổ sung
  • let TryParseNAME _ xử lý ignoreCasevới tham số hiện có (được giới thiệu trong VS2010/.Net 4)
  • tùy chọn sử dụng giá trị chung defaultNAME_ (được giới thiệu trong VS2005/.Net 2)
  • sử dụng đối số tùy chọn (được giới thiệu trong VS2010/.Net 4) với các giá trị mặc định, cho defaultValueignoreCasename__

dẫn đến:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}
30
Yahoo Serious

Bạn có thể định nghĩa một hàm tạo tĩnh cho lớp sẽ kiểm tra xem kiểu T có phải là enum không và ném ngoại lệ nếu không. Đây là phương pháp được Jeffery Richter đề cập trong cuốn sách CLR của mình thông qua C #.

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

Sau đó, trong phương thức phân tích cú pháp, bạn chỉ có thể sử dụng Enum.Pude (typeof (T), input, true) để chuyển đổi từ chuỗi sang enum. Tham số đúng cuối cùng là bỏ qua trường hợp đầu vào.

18
Karg

Cũng cần xem xét rằng kể từ khi phát hành C # 7.3 sử dụng các ràng buộc Enum được hỗ trợ ngoài luồng mà không phải thực hiện kiểm tra bổ sung và công cụ.

Vì vậy, hãy tiếp tục và cho rằng bạn đã thay đổi phiên bản ngôn ngữ của dự án của bạn thành C # 7.3, đoạn mã sau sẽ hoạt động hoàn toàn tốt:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

Trong trường hợp bạn không biết cách thay đổi phiên bản ngôn ngữ thành C # 7.3, hãy xem ảnh chụp màn hình sau: enter image description here

EDIT 1 - Phiên bản Visual Studio bắt buộc và xem xét ReSharper

Để Visual Studio nhận ra cú pháp mới, bạn cần ít nhất phiên bản 15.7. Bạn có thể tìm thấy điều đó cũng được đề cập trong ghi chú phát hành của Microsoft, xem Ghi chú phát hành Visual Studio 2017 15.7 . Cảm ơn @MohamedElshawaf đã chỉ ra câu hỏi hợp lệ này.

Xin cũng lưu ý rằng trong trường hợp của tôi, ReSharper 2018.1 khi viết EDIT này chưa hỗ trợ C # 7.3. Khi ReSharper kích hoạt, nó làm nổi bật ràng buộc Enum là một lỗi cho tôi biết Không thể sử dụng 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' như loại ràng buộc tham số . ReSharper đề xuất như là một sửa chữa nhanh để Xóa ràng buộc 'Enum' của kiểu tham số T của phương thức

Tuy nhiên, nếu bạn tạm thời tắt ReSharper trong Công cụ -> Tùy chọn -> ReSharper Ultimate -> Chung bạn sẽ thấy cú pháp hoàn toàn tốt nếu bạn sử dụng VS 15.7 trở lên và C # 7.3 trở lên.

13
baumgarb

Tôi đã sửa đổi mẫu bằng dimarzionist. Phiên bản này sẽ chỉ hoạt động với Enums và không cho phép cấu trúc đi qua.

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}
11
Bivoauc

Tôi đã cố gắng cải thiện mã một chút:

public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible
{
    if (Enum.IsDefined(typeof(T), value))
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
    return defaultValue;
}
9
Martin

Tôi có yêu cầu cụ thể khi tôi yêu cầu sử dụng enum với văn bản được liên kết với giá trị enum. Ví dụ: khi tôi sử dụng enum để chỉ định loại lỗi, nó được yêu cầu để mô tả chi tiết lỗi.

public static class XmlEnumExtension
{
    public static string ReadXmlEnumAttribute(this Enum value)
    {
        if (value == null) throw new ArgumentNullException("value");
        var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true);
        return attribs.Length > 0 ? attribs[0].Name : value.ToString();
    }

    public static T ParseXmlEnumAttribute<T>(this string str)
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true);
            if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item;
        }
        return (T)Enum.Parse(typeof(T), str, true);
    }
}

public enum MyEnum
{
    [XmlEnum("First Value")]
    One,
    [XmlEnum("Second Value")]
    Two,
    Three
}

 static void Main()
 {
    // Parsing from XmlEnum attribute
    var str = "Second Value";
    var me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    // Parsing without XmlEnum
    str = "Three";
    me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    me = MyEnum.One;
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
}
5
Sunny Rajwadi

Hy vọng điều này hữu ích:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}
4
dimarzionist

Đây là mất của tôi tại nó. Kết hợp từ câu trả lời và MSDN

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

Nguồn MSDN

3
KarmaEDV

Thật thú vị, rõ ràng đây là có thể trong các langau khác (Được quản lý trực tiếp C++, IL).

Để trích:

... Cả hai ràng buộc thực sự tạo ra IL hợp lệ và cũng có thể được sử dụng bởi C # nếu được viết bằng ngôn ngữ khác (bạn có thể khai báo các ràng buộc đó trong C++ được quản lý hoặc IL).

Ai biết

3
Andrew Backer

Các câu trả lời hiện có là đúng với C # <= 7.2. Tuy nhiên, có một ngôn ngữ C # yêu cầu tính năng (gắn với một corefx yêu cầu tính năng) để cho phép các điều sau;

public class MyGeneric<TEnum> where TEnum : System.Enum
{ }

Tại thời điểm viết bài, tính năng này là "Đang thảo luận" tại các Cuộc họp Phát triển Ngôn ngữ.

CHỈNH SỬA

Theo thông tin của nawfal , điều này đang được giới thiệu trong C # 7. .

3
DiskJunky

Tôi đã gói giải pháp của Vivek vào một lớp tiện ích mà bạn có thể sử dụng lại. Xin lưu ý rằng bạn vẫn nên xác định các ràng buộc loại "trong đó T: struct, IConvertible" trên loại của bạn.

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}
1
niaher

Tôi luôn thích điều này (bạn có thể sửa đổi khi thích hợp):

public static IEnumerable<TEnum> GetEnumValues()
{
  Type enumType = typeof(TEnum);

  if(!enumType.IsEnum)
    throw new ArgumentException("Type argument must be Enum type");

  Array enumValues = Enum.GetValues(enumType);
  return enumValues.Cast<TEnum>();
}
1
Jeff

Như đã nêu trong các câu trả lời khác trước đây; trong khi điều này không thể được thể hiện bằng mã nguồn, nó thực sự có thể được thực hiện ở cấp độ IL. @Christopher Currens câu trả lời cho thấy IL làm điều đó như thế nào.

Với Fody s Bổ trợ ExtraConstraint.Fody có một cách rất đơn giản, hoàn thành với công cụ xây dựng, để đạt được điều này. Chỉ cần thêm các gói nuget của họ (Fody, ExtraConstraints.Fody) vào dự án của bạn và thêm các ràng buộc như sau (Trích từ Readme của ExtraConstraint):

public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}

public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}

và Fody sẽ thêm IL cần thiết cho sự hạn chế hiện diện. Cũng lưu ý tính năng bổ sung của các đại biểu ràng buộc:

public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}

public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}

Về Enums, bạn cũng có thể muốn lưu ý đến điều rất thú vị Enums.NET .

1
BatteryBackupUnit

Tôi đã tạo một tiện ích mở rộng Phương thức to get integer value from enum hãy xem triển khai phương thức

public static int ToInt<T>(this T soure) where T : IConvertible//enum
{
    if (typeof(T).IsEnum)
    {
        return (int) (IConvertible)soure;// the tricky part
    }
    //else
    //    throw new ArgumentException("T must be an enumerated type");
    return soure.ToInt32(CultureInfo.CurrentCulture);
}

đây là cách sử dụng

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way
1
Basheer AL-MOMANI

Tôi yêu thích giải pháp của Christopher Currens khi sử dụng IL nhưng đối với những người không muốn đối phó với việc kinh doanh phức tạp bao gồm MSIL vào quy trình xây dựng của họ, tôi đã viết chức năng tương tự trong C #.

Xin lưu ý rằng bạn không thể sử dụng hạn chế chung như where T : Enum vì Enum là loại đặc biệt. Vì vậy, tôi phải kiểm tra xem loại chung có thực sự là enum không.

Chức năng của tôi là:

public static T GetEnumFromString<T>(string strValue, T defaultValue)
{
    // Check if it realy enum at runtime 
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Method GetEnumFromString can be used with enums only");

    if (!string.IsNullOrEmpty(strValue))
    {
        IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator();
        while (enumerator.MoveNext())
        {
            T temp = (T)enumerator.Current;
            if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower()))
                return temp;
        }
    }

    return defaultValue;
}
1
expert

Nếu sử dụng truyền trực tiếp sau đó, tôi đoán bạn có thể sử dụng lớp cơ sở System.Enum trong phương thức của mình, bất cứ khi nào cần thiết. Bạn chỉ cần thay thế các tham số loại một cách cẩn thận. Vì vậy, việc thực hiện phương pháp sẽ như sau:

public static class EnumUtils
{
    public static Enum GetEnumFromString(string value, Enum defaultValue)
    {
        if (string.IsNullOrEmpty(value)) return defaultValue;
        foreach (Enum item in Enum.GetValues(defaultValue.GetType()))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

Sau đó, bạn có thể sử dụng nó như:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);
0
uluorta