Nasıl System.Enum'den temel tamsayıya dönüştürebilirim?


96

Herhangi bir System.Enum türetilmiş türü, çevrim yapmadan ve tercihen bir dizeyi ayrıştırmadan karşılık gelen tamsayı değerine dönüştürmek için genel bir yöntem oluşturmak istiyorum.

Örneğin, istediğim şunun gibi bir şey:

// Trivial example, not actually what I'm doing.
class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        (int)anEnum;
    }
}

Ancak bu işe yaramıyor gibi görünüyor. Resharper, 'int' yazmak için 'System.Enum' türündeki ifadeyi çeviremeyeceğinizi bildiriyor.

Şimdi bu çözümü buldum ama daha verimli bir şeyi tercih ederim.

class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        return int.Parse(anEnum.ToString("d"));
    }
}

Herhangi bir öneri?


1
Resharper değil, şikayet eden derleyici olduğuna inanıyorum.
Kugel

1
Şart değil. System. Kodu derleyip çalıştırırsam gayet iyi çalışıyor. Daha sonra VS'yi kapatırsam, Resharper önbelleğini uçurursam ve yeniden çalıştırırsam, yeniden tarama tamamlandıktan sonra her şey yolunda gider. Benim için bu bir tür önbellek sorunu. Onun için aynı olabilir.
Mir

@Mir ReSharper'ın bu konuda "şikayetçi" oldum. Benim için aynı düzeltme. Neden bu türleri karıştırdığından emin değilim, ama kesinlikle derleyici değil.
akousmata

Yanıtlar:


137

Oyunculuk yapmak istemiyorsanız

Convert.ToInt32()

hile yapabilirdi.

Doğrudan yayın (aracılığıyla (int)enumValue) mümkün değildir. Bir enum farklı altta yatan türlerini olabileceğinden bu da "tehlikeli" olacağını Not ( int, long, byte...).

Daha resmi olarak: System.Enumile doğrudan miras ilişkisi yoktur Int32(her ikisi de ValueTypes'dir), bu nedenle açık atama, tür sistemi içinde doğru olamaz


3
Converter.ToInteger(MyEnum.MyEnumConstant);burada size hiçbir hata vermeyecektir. Lütfen bu bölümü düzenleyin.
nawfal

Teşekkürler @ nawfal, haklısın. Yanıtı düzenledim (ve yeni bir şey öğrendim :) ...)
MartinStettner 13

Temel türden farklıysa çalışmıyorint
Alex Zhukovskiy

@YaroslavSivakov, örneğin longenum için çalışmayacak .
Alex Zhukovskiy

System.Enumbir sınıftır, bu yüzden bir referans türüdür. Değerler muhtemelen kutulanmıştır.
Teejay

44

Bir nesneye ve ardından bir int'e çevirerek çalışmasını sağladım:

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return (int)((object)enumValue);
    }
}

Bu çirkin ve muhtemelen en iyi yol değil. Daha iyi bir şey bulabilir miyim diye bakmaya devam edeceğim ...

DÜZENLEME: Convert.ToInt32'nin (enumValue) de çalıştığını göndermek üzereydim ve MartinStettner'ın beni yendiğini fark etti.

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

Ölçek:

int x = DayOfWeek.Friday.ToInt();
Console.WriteLine(x); // results in 5 which is int value of Friday

DÜZENLEME 2: Yorumlarda, birisi bunun yalnızca C # 3.0'da çalıştığını söyledi. Bunu VS2005'te şu şekilde test ettim ve işe yaradı:

public static class Helpers
{
    public static int ToInt(Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

    static void Main(string[] args)
    {
        Console.WriteLine(Helpers.ToInt(DayOfWeek.Friday));
    }

Sana +1 verdim ama bu çözüm sadece C # 3.0 ve üstü ile çalışacak.
Vadim

Sanırım bu sadece derleyiciyi tatmin ediyor. Bunu çalışma zamanında test etmeyi başardınız mı? Bu işleve herhangi bir değer
aktaramadım

Bir uzatma yöntemi olduğu için mi? Veya C # 'ın eski sürümlerindeki numaralandırmalarla ilgili farklı bir şey mi var?
BFree

Gönderimin sonuna bir test ekledim. Bunu denedim ve benim için çalıştı.
BFree

@BFree: Görünüşe göre sadece uzatma yöntemleriyle çalışıyor. Bunu "eski tarz" bir işlevle denedim (C # 2.0'da) ve ona herhangi bir değer
iletecek

15

Herhangi bir numaralandırmayı temel alınan türe dönüştürmeniz gerekiyorsa (tüm numaralandırmalar tarafından desteklenmez int), o zaman şunları kullanabilirsiniz:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));

5
Bu tek kurşun geçirmez cevap.
Rudey

Bu, boks kutusunun açılmasına neden olur mu?
b.ben

@ b.ben evet. ilk argümanı Convert.ChangeTypekabul eder objectve geri döner object.
Drew Noakes

Evet, gerçekten bu konuda @Rudey'e katılıyorum, nasılsa performans testleri yapmalısınız. BFree'nin çözümü açık ara en hızlı olanıdır. Yaklaşık sekiz kat daha hızlı. Her neyse, hala kenelerden bahsediyoruz.
Yirmi

10

Tekerleği neden yardımcı bir yöntemle yeniden icat etmeniz gerekiyor? enumTemel türüne bir değer biçmek tamamen yasaldır .

Daha az yazıyor ve bence kullanımı daha okunaklı ...

int x = (int)DayOfWeek.Tuesday;

... gibi bir şey yerine ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();

6
HERHANGİ bir enum değerini dönüştürmek istiyorum. Yani, bazı kodlar tarafından genel bir şekilde işlenen sıralama alanları olan çeşitli sınıflarım var. Bu nedenle int'e basit bir çevrim uygun değildir.
orj

@orj, ne demek istediğinden pek emin değilim. İhtiyacınız olan kullanımı gösteren bir örnek verebilir misiniz?
LukeH

2
Bazen, genel bir yöntem durumunda olduğu gibi, bir numaralandırmayı doğrudan yapamazsınız. Genel yöntemler numaralandırmalarla sınırlandırılamadığından, onu bir int'e dönüştürülemeyen struct gibi bir şeyle sınırlandırabilirsiniz. Bu durumda, bunu yapmak için Convert.ToInt32 (enum) kullanabilirsiniz.
Daniel T.

ToInteger () uzantı yönteminin ToString () ile aynı biçimi izlediği fikrini beğendim. Bir yandan int değerini istediğinizde int'e çevirmenin daha az okunabilir olduğunu ve özellikle kod yan yana olduğunda string değerine ihtiyacımız olduğunda ToString () kullanmanın daha az okunabilir olduğunu düşünüyorum.
Louise Eggleton

Ayrıca, bir uzantı yöntemi kullanmak kod tabanınıza tutarlılık katabilir. @Nawfal'ın işaret ettiği gibi, bir numaralandırmadan int değerini almanın birkaç yolu olduğundan, bir uzantı yöntemi oluşturmak ve kullanımını zorunlu kılmak, bir numaralandırmanın int değerini her aldığınızda aynı yöntemi kullandığınız anlamına gelir.
Louise Eggleton

4

Cevabım burada :

Verildiği egibi:

Enum e = Question.Role;

Sonra bunlar çalışır:

int i = Convert.ToInt32(e);
int i = (int)(object)e;
int i = (int)Enum.Parse(e.GetType(), e.ToString());
int i = (int)Enum.ToObject(e.GetType(), e);

Son ikisi düpedüz çirkin. Birincisi daha okunaklı olmalı, ikincisi ise çok daha hızlı. Veya bir uzatma yöntemi, her iki dünyanın en iyisi, en iyisi olabilir.

public static int GetIntValue(this Enum e)
{
    return e.GetValue<int>();
}

public static T GetValue<T>(this Enum e) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
    return (T)(object)e;
}

Şimdi arayabilirsin:

e.GetValue<int>(); //or
e.GetIntValue();

İkincisinin daha hızlı olduğundan emin misin? Numaralamayı kutuya alacağını ve ardından int'e geri döneceğini varsayıyorum?
yoyo

1
@yoyo edeğişken zaten gerçek değer türünün kutulu örneğine sahip, yani enum. Yazdığın zaman Enum e = Question.Role;zaten kutulu. Soru, kutulu System.Enumyeniden temeldeki int türüne dönüştürmeyle ilgilidir (kutudan çıkarma). Yani burada kutudan çıkarma sadece performans meselesidir. (int)(object)edoğrudan kutudan çıkarma çağrısıdır; evet, diğer yaklaşımlardan daha hızlı olmalıdır. Bunu görün
nawfal

açıklama için teşekkürler. Bir System.Enum örneğinin kutulanmamış bir değer olduğu yanlış izlenimine kapılmıştım. Bunu da görün - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo

@yoyo hmm evet. Bağlantıdan ilgili alıntı: Herhangi bir enum türünden System.Enum türüne.
nawfal

1

Bir dökümler System.Enumbir karşı int(o da var benim için cezası çalışır MSDN ). Belki de bir Yeniden Paylaşım hatasıdır.


1
Hangi çalışma zamanı sürümünü kullanıyorsunuz?
JoshBerke

2
bağlantı kendi başına System.Enumdeğil alt türlerinin dökümünü gösterir System.Enum.
nawfal

Benzer bir sorun, Resharper önbellek sorunuydu. VS'yi kapatmak ve Yeniden Paylaşım önbelleğini silmek, tam yeniden taramadan sonra bile hatayı ortadan kaldırdı.
Mir

1

Enumlar bayt, sbyte, short, ushort, int, uint, long ve ulong ile sınırlandırıldığından, bazı varsayımlar yapabiliriz.

Mevcut en büyük kapsayıcıyı kullanarak dönüştürme sırasında istisnaları önleyebiliriz. Ne yazık ki, hangi konteynerin kullanılacağı net değil çünkü ulong, negatif sayılar için patlayacak ve long.MaxValue ile ulong.MaxValue arasındaki sayılar için uzun patlayacak. Temel türe göre bu seçimler arasında geçiş yapmamız gerekiyor.

Elbette, sonuç int içine sığmadığında ne yapacağına karar vermen gerekiyor. Bence oyuncu seçimi sorun değil, ancak hala bazı sorunlar var:

  1. int'den (long ve ulong) daha büyük bir alan boşluğuna sahip bir türe dayalı numaralandırmalar için, bazı numaralandırmaların aynı değeri değerlendirmesi mümkündür.
  2. int.MaxValue değerinden daha büyük bir sayının çevrilmesi, işaretli bir bölgedeyseniz bir istisna atacaktır.

İşte benim önerim, bu işlevi nerede ifşa edeceğime karar vermeyi okuyucuya bırakacağım; yardımcı veya uzantı olarak.

public int ToInt(Enum e)
{
  unchecked
  {
    if (e.GetTypeCode() == TypeCode.UInt64)
      return (int)Convert.ToUInt64(e);
    else
      return (int)Convert.ToInt64(e);
  }
}

-1

Enum türünün kendisinde bir grup statik yardımcı işlev bulunduğunu unutmayın. Tek yapmak istediğiniz, numaralandırmanın bir örneğini karşılık gelen tam sayı türüne dönüştürmekse, çevrim muhtemelen en verimli yoldur.

Bence ReSharper şikayet ediyor çünkü Enum herhangi bir türden bir numara değildir ve numaralandırmaların kendisi Enum'dan değil, skaler bir değer türünden türetilir. Genel bir şekilde uyarlanabilir döküm ihtiyacınız varsa, bunun size uygun olabileceğini söyleyebilirim (numaralandırma türünün kendisinin de jenerikte yer aldığını unutmayın:

public static EnumHelpers
{
    public static T Convert<T, E>(E enumValue)
    {
        return (T)enumValue;
    }
}

Bu daha sonra şu şekilde kullanılabilir:

public enum StopLight: int
{
    Red = 1,
    Yellow = 2,
    Green = 3
}

// ...

int myStoplightColor = EnumHelpers.Convert<int, StopLight>(StopLight.Red);

Kafamın tepesinden kesin olarak söyleyemem, ancak yukarıdaki kod, aşağıdakilere izin veren C # 'ın tür çıkarımı tarafından destekleniyor olabilir:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);

1
Maalesef, örneğinizde E herhangi bir tür olabilir. Jenerikler, Enum'u tür parametresi kısıtlaması olarak kullanmama izin vermiyor. Söyleyemem: nerede T: Enum
Vadim

Yine de T: struct'ı kullanabilirsiniz. İstediğiniz kadar katı değil, ancak herhangi bir referans türünü (ki sizin yapmaya çalıştığınız şey olduğunu düşünüyorum)
jrista

şunları kullanabilirsiniz: if (! typeof (E) .IsEnum) yeni ArgumentException ("E, numaralandırılmış bir tür olmalıdır");
diadiora

1
Bu uzaktan bile doğru değil, statik yardımcı geçerli bile değil.
Nicholi

1
Neden EnumHelpers.Convert<int, StopLight>(StopLight.Red);bunun yerine biri kullanmalı (int)StopLight.Read;? Ayrıca soru bundan bahsediyor System.Enum.
nawfal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.