chuyển đổi một enum sang một loại enum khác


120

Tôi có một enum của ví dụ ' Gender' ( Male =0 , Female =1) và tôi có một enum khác từ một dịch vụ có enum Gender của riêng nó ( Male =0 , Female =1, Unknown =2)

Câu hỏi của tôi là làm thế nào tôi có thể viết một cái gì đó nhanh chóng và tốt đẹp để chuyển đổi từ enum của họ thành của tôi?


6
Bạn muốn chuyển đổi "chưa biết" thành gì?
Pavel Minaev

Bạn có thể định kiểu enum để loại enum khác khi cả hai đều có cùng một giá trị nhìn thấy ideone.com/7lgvgf
Gowtham S

Câu trả lời:


87

Sử dụng một phương thức mở rộng hoạt động khá gọn gàng, khi sử dụng hai phương pháp chuyển đổi do Nate đề xuất:

public static class TheirGenderExtensions
{
    public static MyGender ToMyGender(this TheirGender value)
    {
        // insert switch statement here
    }
}

public static class MyGenderExtensions
{
    public static TheirGender ToTheirGender(this MyGender value)
    {
        // insert switch statement here
    }
}

Rõ ràng là không cần sử dụng các lớp riêng biệt nếu bạn không muốn. Sở thích của tôi là giữ các phương thức mở rộng được nhóm theo các lớp / cấu trúc / kiểu liệt kê mà chúng áp dụng.


233

Đã cho Enum1 value = ..., thì nếu bạn muốn nói đến tên:

Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());

Nếu bạn muốn nói đến giá trị số, bạn thường có thể truyền:

Enum2 value2 = (Enum2)value;

(Tuy nhiên, với diễn viên, bạn có thể muốn dùng Enum.IsDefinedđể kiểm tra các giá trị hợp lệ)


16
Đây là câu trả lời hay hơn
Nicholas

1
Đây là một phiên bản sử dụng Enum.Tryparse: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; Điều này sẽ cho phép bạn xử lý các giá trị đầu vào không tồn tại trong đó Enum2mà không cần gọi Enum.IsDefinedhoặc bắt ArgumentExceptioncác giá trị ném qua Enum.Parse. Lưu ý rằng thứ tự của các tham số ít nhiều bị đảo ngược từ Enum.Parse.
Sander

47

Chỉ cần ép kiểu một thành int và sau đó truyền nó sang enum khác (xem xét rằng bạn muốn ánh xạ được thực hiện dựa trên giá trị):

Gender2 gender2 = (Gender2)((int)gender1);

3
Mặc dù khó có thể nhìn thấy nó 'trong tự nhiên' và rất khó xảy ra với các giới tính, nhưng có thể tồn tại một số enum được hỗ trợ bởi một long(hoặc ulong) chứ không phải là một enum intcó các thành viên được xác định ở trên int.MaxValue(hoặc dưới int.MinValue), trong trường hợp đó quá trình ép kiểu intcó thể tràn và bạn sẽ kết thúc với một giá trị enum không xác định mà giá trị này phải được xác định.
Rich O'Kelly

tất nhiên. cách đúng sẽ là (Gender2) ((chèn loại cơ bản ở đây) world1) nhưng tôi nghĩ ví dụ trên đưa ra ý tưởng đúng nên tôi sẽ không thay đổi nó.
Adrian Zanescu

3
Điều này yêu cầu hai enum phải có cùng giá trị theo cùng một thứ tự. Mặc dù nó giải quyết được vấn đề cụ thể này, nhưng điều này thực sự rất khó và tôi sẽ không sử dụng nó cho việc lập bản đồ enum nói chung.
sonicblis

2
tốt .... duh! . Lập bản đồ cần được thực hiện dựa trên một cái gì đó. Trong trường hợp này, ánh xạ là giá trị tích phân. Để lập bản đồ dựa trên tên, bạn cần mã khác. Đối với một loại ánh xạ khác. Noone nói đây là "để lập bản đồ enum nói chung" và trường hợp đó không tồn tại, trừ khi bạn có thể thử để xác định những gì "bản đồ nói chung" có nghĩa là
Adrian Zanescu

20

Để kỹ lưỡng, tôi thường tạo một cặp hàm, một hàm lấy Enum 1 và trả về Enum 2 và một hàm khác nhận Enum 2 và trả về Enum 1. Mỗi hàm bao gồm một câu lệnh trường hợp ánh xạ đầu vào đến đầu ra và trường hợp mặc định ném một ngoại lệ với một tin nhắn phàn nàn về một giá trị không mong đợi.

Trong trường hợp cụ thể này, bạn có thể tận dụng lợi thế của thực tế là các giá trị nguyên của Nam và Nữ là như nhau, nhưng tôi sẽ tránh điều đó vì nó khó hiểu và có thể bị phá vỡ nếu enum thay đổi trong tương lai.


7
+1 Tôi đã thấy nhiều nhà phát triển từ bỏ mong muốn sử dụng giá trị số nguyên của enum để chuyển đổi chúng nhưng điều này rất dễ xảy ra lỗi. Phương pháp học cũ của văn bản 2 chức năng đã chứng minh giá trị của nó theo thời gian ...
Hemant

20

Nếu chúng ta có:

enum Gender
{
    M = 0,
    F = 1,
    U = 2
}

enum Gender2
{
    Male = 0,
    Female = 1,
    Unknown = 2
}

Chúng tôi có thể làm một cách an toàn

var gender = Gender.M;
var gender2   = (Gender2)(int)gender;

Hoặc thậm chí

var enumOfGender2Type = (Gender2)0;

Nếu bạn muốn đề cập đến trường hợp enum ở bên phải của dấu '=' có nhiều giá trị hơn enum ở bên trái - bạn sẽ phải viết phương thức / từ điển của riêng mình để che điều đó như những người khác đã đề xuất.


Câu trả lời của bạn giống như một câu hỏi !? Nếu có thì đây không phải là câu trả lời và nếu không có câu trả lời tương tự ở trên ;).
shA.t

13

Bạn có thể viết một phương thức mở rộng chung đơn giản như thế này

public static T ConvertTo<T>(this object value)            
    where T : struct,IConvertible
{
    var sourceType = value.GetType();
    if (!sourceType.IsEnum)
        throw new ArgumentException("Source type is not enum");
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Destination type is not enum");
    return (T)Enum.Parse(typeof(T), value.ToString());
}

1
Nó không bao gồm trường hợp thiếu giá trị như được đề xuất trong các câu trả lời ở trên. Bạn cũng nên sửa đổi phương pháp mở rộng này cho trường hợp đó.
eRaisedToX

8

bạn có thể viết một hàm đơn giản như sau:

public static MyGender ConvertTo(TheirGender theirGender)
{
    switch(theirGender)
    {
        case TheirGender.Male:
            break;//return male
        case TheirGender.Female:
            break;//return female
        case TheirGender.Unknown:
            break;//return whatever
    }
}

1
đây không phải là một chức năng. dự kiến ​​'MyGender' và bạn sẽ trả lại 'void'
bl4ckr0se

7

Đây là phiên bản phương thức mở rộng nếu ai quan tâm

public static TEnum ConvertEnum<TEnum >(this Enum source)
    {
        return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
    }

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();

Điều đó không ngụ ý rằng cả hai kiểu liệt kê có cùng giá trị số?
kuskmen

1
Không, đây là chuyển đổi theo tên theo chuỗi. Vì vậy Enum.Foo (1) sẽ dịch sang Enum2.Foo (2) mặc dù giá trị số của chúng khác nhau.
Justin

3
public static TEnum ConvertByName<TEnum>(this Enum source, bool ignoreCase = false) where TEnum : struct
{
    // if limited by lack of generic enum constraint
    if (!typeof(TEnum).IsEnum)
    {
        throw new InvalidOperationException("enumeration type required.");
    }

    TEnum result;
    if (!Enum.TryParse(source.ToString(), ignoreCase, out result))
    {
        throw new Exception("conversion failure.");
    }

    return result;
}

2

Tôi đã viết một tập hợp các phương thức mở rộng cách đây một thời gian hoạt động cho một số loại khác nhau Enum. Một công cụ cụ thể hoạt động cho những gì bạn đang cố gắng hoàn thành và xử lý Enumcác phần mềm FlagsAttributecũng như Enumcác phần tử cơ bản khác nhau.

public static tEnum SetFlags<tEnum>(this Enum e, tEnum flags, bool set, bool typeCheck = true) where tEnum : IComparable
{
    if (typeCheck)
    {
        if (e.GetType() != flags.GetType())
            throw new ArgumentException("Argument is not the same type as this instance.", "flags");
    }

    var flagsUnderlyingType = Enum.GetUnderlyingType(typeof(tEnum));

    var firstNum = Convert.ToUInt32(e);
    var secondNum = Convert.ToUInt32(flags);

    if (set)
        firstNum |= secondNum;

    else
        firstNum &= ~secondNum;

    var newValue = (tEnum)Convert.ChangeType(firstNum, flagsUnderlyingType);

    if (!typeCheck)
    {
        var values = Enum.GetValues(typeof(tEnum));
        var lastValue = (tEnum)values.GetValue(values.Length - 1);

        if (newValue.CompareTo(lastValue) > 0)
            return lastValue;
    }

    return newValue;
}

Từ đó bạn có thể thêm các phương pháp mở rộng khác cụ thể hơn.

public static tEnum AddFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, true);
}

public static tEnum RemoveFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, false);
}

Điều này sẽ thay đổi các kiểu Enumgiống như bạn đang cố gắng làm.

public static tEnum ChangeType<tEnum>(this Enum e) where tEnum : IComparable
{
    return SetFlags(e, default(tEnum), true, false);
}

Tuy nhiên, hãy cảnh báo rằng bạn CÓ THỂ chuyển đổi giữa bất kỳ Enumvà bất kỳ Enumphương pháp nào khác bằng phương pháp này, ngay cả những phương pháp không có cờ. Ví dụ:

public enum Turtle
{
    None = 0,
    Pink,
    Green,
    Blue,
    Black,
    Yellow
}

[Flags]
public enum WriteAccess : short
{
   None = 0,
   Read = 1,
   Write = 2,
   ReadWrite = 3
}

static void Main(string[] args)
{
    WriteAccess access = WriteAccess.ReadWrite;
    Turtle turtle = access.ChangeType<Turtle>();
}

Biến turtlesẽ có giá trị là Turtle.Blue.

Tuy nhiên, có sự an toàn từ Enumcác giá trị không xác định bằng cách sử dụng phương pháp này. Ví dụ:

static void Main(string[] args)
{
    Turtle turtle = Turtle.Yellow;
    WriteAccess access = turtle.ChangeType<WriteAccess>();
}

Trong trường hợp này, accesssẽ được đặt thành WriteAccess.ReadWrite, vìWriteAccess Enum có giá trị lớn nhất là 3.

Một tác dụng phụ khác của việc trộn Enums với FlagsAttributevà những không có nó là quá trình chuyển đổi sẽ không dẫn đến kết quả khớp 1-1 giữa các giá trị của chúng.

public enum Letters
{
    None = 0,
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H
}

[Flags]
public enum Flavors
{
    None = 0,
    Cherry = 1,
    Grape = 2,
    Orange = 4,
    Peach = 8
}

static void Main(string[] args)
{
    Flavors flavors = Flavors.Peach;
    Letters letters = flavors.ChangeType<Letters>();
}

Trong trường hợp này, letterssẽ có giá trị Letters.Hthay vì Letters.D, vì giá trị hỗ trợ của Flavors.Peachlà 8. Ngoài ra, một chuyển đổi từ Flavors.Cherry | Flavors.Grapethành Letterssẽ mang lại lợi nhuận Letters.C, điều này có vẻ không trực quan.


2

Dựa trên câu trả lời của Justin ở trên, tôi đã nghĩ ra điều này:

    /// <summary>
    /// Converts Enum Value to different Enum Value (by Value Name) See https://stackoverflow.com/a/31993512/6500501.
    /// </summary>
    /// <typeparam name="TEnum">The type of the enum to convert to.</typeparam>
    /// <param name="source">The source enum to convert from.</param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public static TEnum ConvertTo<TEnum>(this Enum source)
    {
        try
        {
            return (TEnum) Enum.Parse(typeof(TEnum), source.ToString(), ignoreCase: true);
        }
        catch (ArgumentException aex)
        {
            throw new InvalidOperationException
            (
                $"Could not convert {source.GetType().ToString()} [{source.ToString()}] to {typeof(TEnum).ToString()}", aex
            );
        }
    }

1

Tôi biết đó là một câu hỏi cũ và có rất nhiều câu trả lời, Tuy nhiên, tôi thấy rằng việc sử dụng câu lệnh switch như trong câu trả lời được chấp nhận là hơi rườm rà, vì vậy đây là 2 xu của tôi:

Phương pháp yêu thích của cá nhân tôi là sử dụng từ điển, trong đó khóa là enum nguồn và giá trị là enum đích - vì vậy trong trường hợp được trình bày trên câu hỏi, mã của tôi sẽ như thế này:

var genderTranslator = new Dictionary<TheirGender, MyGender>();
genderTranslator.Add(TheirGender.Male, MyGender.Male);
genderTranslator.Add(TheirGender.Female, MyGender.Female);
genderTranslator.Add(TheirGender.Unknown, MyGender.Unknown);

// translate their to mine    
var myValue = genderTranslator[TheirValue];

// translate mine to their
var TheirValue = genderTranslator .FirstOrDefault(x => x.Value == myValue).Key;;

Tất nhiên, điều này có thể được bao bọc trong một lớp tĩnh và được sử dụng như một phương thức mở rộng:

public static class EnumTranslator
{

    private static Dictionary<TheirGender, MyGender> GenderTranslator = InitializeGenderTranslator();

    private static Dictionary<TheirGender, MyGender> InitializeGenderTranslator()
    {
        var translator = new Dictionary<TheirGender, MyGender>();
        translator.Add(TheirGender.Male, MyGender.Male);
        translator.Add(TheirGender.Female, MyGender.Female);
        translator.Add(TheirGender.Unknown, MyGender.Unknown);
        return translator;
    }

    public static MyGender Translate(this TheirGender theirValue)
    {
        return GenderTranslator[theirValue];
    }

    public static TheirGender Translate(this MyGender myValue)
    {
        return GenderTranslator.FirstOrDefault(x => x.Value == myValue).Key;
    }

}

Tôi thích cách tiếp cận này vì bạn cũng có thể liệt kê cả hai cách liệt kê để điền từ điển. (tất nhiên là khi chúng có cùng thứ tự)
AlexS

0

Bạn có thể sử dụng ToString () để chuyển đổi enum đầu tiên thành tên của nó, và sau đó Enum.Parse () để chuyển đổi chuỗi trở lại Enum khác. Điều này sẽ ném ra một ngoại lệ nếu giá trị không được hỗ trợ bởi enum đích (tức là đối với một giá trị "Không xác định")

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.