İnt'i geçersiz enum değerine çevirmek neden istisna atmaz?


124

Böyle bir numaram varsa:

enum Beer
{
    Bud = 10,
    Stella = 20,
    Unknown
}

intBu değerlerin dışında olanı bir türüne çevirirken neden bir istisna atmıyor Beer?

Örneğin, aşağıdaki kod bir istisna oluşturmaz, konsola '50' çıktısı verir:

int i = 50;
var b = (Beer) i;

Console.WriteLine(b.ToString());

Bunu garip buluyorum ... kimse açıklayabilir mi?


27
Enum.IsDefined ile bir değerin geçerli olup olmadığını her zaman kontrol edebileceğinizi unutmayın .
Tim Schmelter

havalı Bunu bilmiyordum, muhtemelen şu anda çözmeye çalıştığım problemde kullanacağım
jcvandan


1
Stella'nın Bud'dan iki kat daha değerli olması için oy verildi.
Dan Bechard

Yanıtlar:


81

Bir Enum'un ayrıştırılmasıyla ilgili kafa karışıklığından alınmıştır

Bu, .NET'i oluşturan kişiler tarafından alınan bir karardı. Bir enum başka değer türü (tarafından desteklenmektedir int, short, byteaslında bu değer türleri için geçerli olan herhangi bir değere sahip olabilir, böylece, vs) ve.

Ben şahsen bunun çalışma şeklinin hayranı değilim, bu yüzden bir dizi yardımcı yöntem yaptım:

/// <summary>
/// Utility methods for enum values. This static type will fail to initialize 
/// (throwing a <see cref="TypeInitializationException"/>) if
/// you try to provide a value that is not an enum.
/// </summary>
/// <typeparam name="T">An enum type. </typeparam>
public static class EnumUtil<T>
    where T : struct, IConvertible // Try to get as much of a static check as we can.
{
    // The .NET framework doesn't provide a compile-checked
    // way to ensure that a type is an enum, so we have to check when the type
    // is statically invoked.
    static EnumUtil()
    {
        // Throw Exception on static initialization if the given type isn't an enum.
        Require.That(typeof (T).IsEnum, () => typeof(T).FullName + " is not an enum type.");
    }

    /// <summary>
    /// In the .NET Framework, objects can be cast to enum values which are not
    /// defined for their type. This method provides a simple fail-fast check
    /// that the enum value is defined, and creates a cast at the same time.
    /// Cast the given value as the given enum type.
    /// Throw an exception if the value is not defined for the given enum type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumValue"></param>
    /// <exception cref="InvalidCastException">
    /// If the given value is not a defined value of the enum type.
    /// </exception>
    /// <returns></returns>
    public static T DefinedCast(object enumValue)

    {
        if (!System.Enum.IsDefined(typeof(T), enumValue))
            throw new InvalidCastException(enumValue + " is not a defined value for enum type " +
                                           typeof (T).FullName);
        return (T) enumValue;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="enumValue"></param>
    /// <returns></returns>
    public static T Parse(string enumValue)
    {
        var parsedValue = (T)System.Enum.Parse(typeof (T), enumValue);
        //Require that the parsed value is defined
        Require.That(parsedValue.IsDefined(), 
            () => new ArgumentException(string.Format("{0} is not a defined value for enum type {1}", 
                enumValue, typeof(T).FullName)));
        return parsedValue;
    }

    public static bool IsDefined(T enumValue)
    {
        return System.Enum.IsDefined(typeof (T), enumValue);
    }

}


public static class EnumExtensions
{
    public static bool IsDefined<T>(this T enumValue)
        where T : struct, IConvertible
    {
        return EnumUtil<T>.IsDefined(enumValue);
    }
}

Bu şekilde şunu söyleyebilirim:

if(!sEnum.IsDefined()) throw new Exception(...);

... veya:

EnumUtil<Stooge>.Parse(s); // throws an exception if s is not a defined value.

Düzenle

Yukarıda verilen açıklamanın ötesinde, Enum'un .NET sürümünün Java'dan esinlenenden daha C'den esinlenen bir model izlediğini anlamalısınız. Bu, belirli bir "işaretin" bir enum değerinde aktif olup olmadığını belirlemek için ikili örüntüleri kullanabilen "Bit İşareti" numaralandırmalarına sahip olmayı mümkün kılar . Her olası bayrak kombinasyonunu tanımlamanız gerekirse (yani MondayAndTuesday, MondayAndWednesdayAndThursday), bunlar son derece sıkıcı olurdu. Bu nedenle, tanımsız enum değerlerini kullanma kapasitesine sahip olmak gerçekten kullanışlı olabilir. Bu tür hilelerden yararlanmayan enum türlerinde hızlı bir davranış istediğinizde, sadece biraz daha fazla çalışma gerektirir.


Güzel ... Ben de çalışma şeklinin hayranı değilim, bana tuhaf geliyor
jcvandan

3
@dormisher: "'Tis delilik, ama yapılmaması gereken bir yöntem var." Düzenlememe bakın.
StriplingWarrior

2
@StriplingWarrior, ilgi için. Java eşdeğeri EnumSet.of'tur (Pazartesi, Çarşamba, Perşembe). EnumSet'in içinde sadece bir tane uzun. Dolayısıyla, çok fazla verimlilik kaybı olmadan güzel bir API verir.
Iain


@StriplingWarrior ayrıştırma yönteminizde bir uzantı yöntemi gibi IsDefined'ı çağırmaya çalışıyorsunuz. Ancak IsDefined yöntemi, EnumUtil sınıfını kısıtlamalarla yapmaya çalıştığınız (çok sevdiğim enum için statik kontrol yapmak için) nedeniyle bir genişletme yöntemi yapılamaz. ancak gerçekte bu bana bir genel sınıf üzerinde bir genişletme yöntemi yapılamayacağını söylüyor. Herhangi bir fikir?
CJC

55

Numaralandırmalar genellikle işaretler olarak kullanılır:

[Flags]
enum Permission
{
    None = 0x00,
    Read = 0x01,
    Write = 0x02,
}
...

Permission p = Permission.Read | Permission.Write;

P'nin değeri, numaralamanın bir değeri olmayan, ancak açıkça geçerli bir değer olan 3 tamsayısıdır.

Ben şahsen farklı bir çözüm görmeyi tercih ederdim; Her ikisini de "enum" ile birleştirmek yerine iki farklı dil özelliği olarak "bit dizisi" tam sayı türleri ve "bir dizi farklı değer" türü yapma yeteneğine sahip olmayı tercih ederdim. Ancak orijinal dil ve çerçeve tasarımcılarının ortaya çıkardığı şey bu; sonuç olarak, enum'un beyan edilmemiş değerlerinin yasal değerler olmasına izin vermeliyiz.


19
Ancak 3 "açıkça geçerli bir değerdir" türü, OP'nin noktasını güçlendirir. Bu [Bayraklar] özniteliği, derleyiciye tanımlı bir numaralandırmayı veya geçerli bir değeri aramasını söyleyebilir. Sadece söylüyorum'.
LarsTech

15

Kısa cevap: Dil tasarımcıları dili bu şekilde tasarlamaya karar verdiler.

Section 6.2.2: Explicit enumeration conversionsC # Dil Spesifikasyonunun uzun cevabı :

İki tür arasındaki açık bir numaralandırma dönüşümü, katılan herhangi bir numaralandırma türünün o enum türünün temel türü olarak ele alınması ve ardından ortaya çıkan türler arasında örtük veya açık bir sayısal dönüştürme gerçekleştirilmesi yoluyla işlenir. Örneğin, int türüne sahip ve temelini oluşturan bir E enum türü verildiğinde, E'den bayta bir dönüşüm int'ten bayta açık bir sayısal dönüşüm (§6.2.1) ve bayttan E'ye bir dönüşüm olarak işlenir. bayttan int'e örtük bir sayısal dönüşüm (§6.1.2).

Temel olarak, bir dönüştürme işlemi yapılması söz konusu olduğunda , numaralandırmaya temel tür olarak davranılır. Varsayılan olarak, bir numaralandırmanın temel türü şudur Int32, bu, dönüşümün tam olarak bir dönüşüm gibi ele alındığı anlamına gelir Int32. Bu, herhangi bir geçerli intdeğere izin verilebilir anlamına gelir .

Bunun esas olarak performans nedeniyle yapıldığından şüpheleniyorum. Yaparak enumherhangi ayrılmaz tip dönüştürme basit ayrılmaz tipi ve izin CLR ekstra kontrollerinin tümü yapmak gerekmez. Bu, enumbir tamsayı kullanmakla karşılaştırıldığında bir performans kaybının olmadığı anlamına gelir ve bu da onun kullanımını teşvik etmeye yardımcı olur.


Bunu garip buluyor musunuz? Bir numaralamanın ana noktasından birinin, bireysel anlamları da atayabileceğimiz bir dizi tamsayıyı güvenli bir şekilde gruplamak olduğunu düşündüm. Bana göre bir enum türünün herhangi bir tamsayı değerini tutmasına izin vermek, amacını bozar.
jcvandan

@dormisher: Az önce düzenledim ve sonuna biraz ekledim. Peşinde olduğunuz "güvenliği" sağlamanın bir maliyeti vardır. Olduğu söyleniyor, normal kullanımda bu gerçekten bir sorun değil.
Reed Copsey

9

Gönderen belgeler :

Days türündeki bir değişkene, temel alınan tür aralığındaki herhangi bir değer atanabilir; değerler adlandırılmış sabitlerle sınırlı değildir.

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.