在尝试使用或返回枚举值时,是否可以避免使用枚举值?

时间:2021-05-25 20:14:41

If I have the following enum:

如果我有以下全会:

public enum ReturnValue{
    Success = 0,
    FailReason1 = 1,
    FailReason2 = 2
    //Etc...
}

Can I avoid casting when I return, like this:

当我回来的时候,我可以避免选择吗?

public static int main(string[] args){
    return (int)ReturnValue.Success;
}

If not, why isn't an enum value treated as an int by default?

如果不是,为什么枚举值在默认情况下不被视为int ?

8 个解决方案

#1


48  

enums are supposed to be type safe. I think they didn't make them implicitly castable to discourage other uses. Although the framework allows you to assign a constant value to them, you should reconsider your intent. If you primarily use the enum for storing constant values, consider using a static class:

枚举应该是类型安全的。我认为他们并没有让它们隐含地被浇铸以阻止其他用途。虽然框架允许您为它们分配一个常量值,但是您应该重新考虑您的意图。如果您主要使用enum来存储常量值,请考虑使用静态类:

public static class ReturnValue
{
    public const int Success = 0;
    public const int FailReason1 = 1;
    public const int FailReason2 = 2;
    //Etc...
}

That lets you do this.

你可以这么做。

public static int main(string[] args){
    return ReturnValue.Success;
}

EDIT

编辑

When you do want to provide values to an enum is when you want to combine them. See the below example:

当您确实希望向枚举提供值时,就是您希望组合它们的时候。看下面的例子:

[Flags] // indicates bitwise operations occur on this enum
public enum DaysOfWeek : byte // byte type to limit size
{
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64,
    Weekend = Sunday | Saturday,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}

This enum can then be consumed by using bitwise math. See the below example for some applications.

然后可以使用位数学来使用这个枚举。有关一些应用程序,请参见下面的示例。

public static class DaysOfWeekEvaluator
{
    public static bool IsWeekends(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
    }

    public static bool IsAllWeekdays(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
    }

    public static bool HasWeekdays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekdays)) > 0;
    }

    public static bool HasWeekendDays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekend)) > 0;
    }
}

#2


4  

There is no implicit cast because the enum does not have to use int as the underlying type. If your enum used a uint as the underlying type, for instance, there is no implicit cast from uint to int.

没有隐含的转换,因为枚举不需要使用int作为底层类型。如果枚举使用uint作为底层类型,例如,没有从uint到int的隐式转换。

#3


4  

The c# enum is useless.

c# enum是没用的。

You can avoid casting from your type AND constrain the values that can be explicitly cast to your type by making a sealed class, and providing implicit/explicit conversion operators.

您可以避免使用类型转换,并通过创建一个密封的类,并提供隐式/显式转换操作符来约束可以显式转换为类型的值。

  • Provide an implicit operator for converting from your type to a generic int so you don't have to cast.
  • 提供一个隐式运算符,用于将类型转换为通用int类型,这样就不需要强制转换了。
  • Provide an explicit operator for converting from an int to your type, which throws an error if the integer fails to meet the constraint, such as (int x) => (x >= 0 && x <= 2).
  • 提供一个显式运算符,用于将int类型转换为您的类型,如果该整数不能满足约束,则会抛出一个错误,例如(int x) => (x >= 0 & x <= 2)。

If using this technique, create a generic immutable base class such as ConstrainedNumber<T>, which has a constructor that accepts a T value and delegate for the constraint: delegate bool NumberConstraint<T>(T value). The constructor should run the value through the constraint delegate, and throw an exception if it fails to meet the constraint. The base class should also take care of the implicit conversion operation to T, and should handle equality by overloading object.Equals(object) and object.GetHashCode(), defining == and != operators for the type ConstrainedNumber<T>, and implementing IEquatable<T> and IEquatable<ConstrainedNumber<T>>. I also recommend defining an copy constructor for the base class, and all derived types. Cloning can then be implemented cleanly in the base class by retrieving the copy constructor via reflection, but this is entirely optional. You can figure out the ConstrainedNumber<T> implementation yourself, unless I've already posted it on * somewhere.

如果使用此技术,则创建一个通用的不可变基类,如ConstrainedNumber ,该基类具有一个构造函数,该构造函数接受T值并为约束委托:委托bool number约束 (T值)。构造函数应该通过约束委托运行该值,如果该值不满足约束,则抛出异常。基类还应该处理到T的隐式转换操作,并且应该通过重载object. equals (object)和object. gethashcode()来处理等式,为类型ConstrainedNumber 定义=和!=操作符,并实现IEquatable 和IEquatable 。我还建议为基类和所有派生类型定义复制构造函数。然后可以通过反射检索复制构造函数,在基类中干净地实现克隆,但这完全是可选的。您可以自己找出ConstrainedNumber 实现,除非我已经在*上发布过。

You can provide named static readonly values in your derived ConstrainedNumber, so that you can access them just like an enum.

您可以在派生的ConstrainedNumber中提供已命名的静态只读值,以便像枚举一样访问它们。

public sealed class ReturnValue: ConstrainedNumber<int>
{
    public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3);

    public static readonly ReturnValue Success = new ReturnValue(0);
    public static readonly ReturnValue FailReason1 = new ReturnValue(1);
    public static readonly ReturnValue FailReason2 = new ReturnValue(2);

    private ReturnValue( int value ): base( value, constraint ) {}
    private ReturnValue( ReturnValue original ): base (original) {} //may be used to support IClonable implementation in base class
    public static explicit operator ReturnValue( int value )
    {
        switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public.
        {
            case 0: return Success;
            case 1: return FailReason1;
            case 2: return FailReason2;
        }
        throw new ArgumentException( "Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value" );
    }

}

You could use this technique for any constraint. For example, a class called EvenNumber may have a constraint that returns true if the given number is even. In that case, you'd just make your constructors public, and simplify your static conversion operator to just return a new EvenNumber, instead of switching to return one of the limited existing instances.

您可以对任何约束使用这种技术。例如,一个名为EvenNumber的类可能有一个约束,如果给定的数字是偶数,则返回true。在这种情况下,您只需将构造函数公开,并将静态转换操作简化为只返回一个新的偶数,而不是切换到返回一个有限的现有实例。

It could be used like this:

可以这样使用:

EvenNumber x = (EvenNumber)2;
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber."  A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model
int z = x; //implicit conversion, no cast necessary;

#4


2  

Enums and ints are simply not implicitly castable as per the spec (except for the literal 0, which is allowed for comparison tests / assignments / etc). The explicit cast is all that is needed, though.

枚举和int只是不能根据规范隐式地浇铸(除了文字0,允许进行比较测试/赋值/等等)。然而,明确的演员阵容才是最需要的。

#5


0  

No, you can't avoid casting; as to why there's no implicit conversion, I don't know, but there's not.

不,你不能避免铸造;至于为什么没有隐式转换,我不知道,但没有。

#6


0  

Strangely enough, this is not specific to the .NET Framework, but just to C#. As the other commenters have already pointed out, in C# this is basically a specification of the language. The same is not true in VB.NET.

奇怪的是,这不是特定于。net框架,而是c#。正如其他评论者已经指出的,在c#中,这基本上是语言的一个规范。在VB.NET中,情况并非如此。

Check out the MSDN reference page for Enums in VB.NET. Note that you can specify the data type of an enumeration at Enum declaration time.

请查看VB.NET中Enums的MSDN参考页面。注意,您可以在Enum声明时指定枚举的数据类型。

That means, if you really don't want to litter your code with casts to (int), you could write your enumeration in VB.NET, declare it as an integer, then use that Enum from C#.

这意味着,如果您真的不想在代码中添加强制类型转换(int),您可以用VB编写枚举。NET,将它声明为一个整数,然后使用c#中的Enum。

Remember how they told us computers would make our lives so much simpler? :)

还记得他们告诉我们电脑会让我们的生活变得更简单吗?:)

#7


0  

You can ascribe this behaviour to the basic intention behind creating Enumerations... to create a set of named constants that can only have specified (or default) values depending on the underlying type.

您可以将这种行为归因于创建枚举的基本意图……要创建一组命名的常量,这些常量只能根据底层类型指定(或默认)值。

There are two separate issues to consider, as related to your question:

关于你的问题,有两个不同的问题需要考虑:

  1. An Enum value cannot be treated as an int by default because then you would be able to provide any integer and there would be no compile time check to validate that the provided integer does in fact exist as a value in the Enumeration.

    枚举值在默认情况下不能被视为int,因为这样您就可以提供任何整数,并且没有编译时检查来验证所提供的整数实际上是否作为枚举中的值存在。

  2. Casting becomes necessary since you are trying to convert from the governing type (of type YourCustomEnum which derives from the System.Enum class) to the underlying type, i.e., int or byte, etc.

    由于您试图从管理类型转换(从系统派生的自定义枚举类型),因此需要进行转换。枚举类型)到底层类型,即, int或byte,等等。

#8


-2  

How about using static Members of a Class?

如何使用类的静态成员?

//enum DocInfos { DocName, DocNumber, DocVersion};
public class DocInfos
{
    public static int DocName = 0;
    public static int DocNumer = 1;
    public static int DocVersion = 2;
}

...

            Doc = new string[DocInfos.DocVersion];
            // Treffer
            Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2])

...

#1


48  

enums are supposed to be type safe. I think they didn't make them implicitly castable to discourage other uses. Although the framework allows you to assign a constant value to them, you should reconsider your intent. If you primarily use the enum for storing constant values, consider using a static class:

枚举应该是类型安全的。我认为他们并没有让它们隐含地被浇铸以阻止其他用途。虽然框架允许您为它们分配一个常量值,但是您应该重新考虑您的意图。如果您主要使用enum来存储常量值,请考虑使用静态类:

public static class ReturnValue
{
    public const int Success = 0;
    public const int FailReason1 = 1;
    public const int FailReason2 = 2;
    //Etc...
}

That lets you do this.

你可以这么做。

public static int main(string[] args){
    return ReturnValue.Success;
}

EDIT

编辑

When you do want to provide values to an enum is when you want to combine them. See the below example:

当您确实希望向枚举提供值时,就是您希望组合它们的时候。看下面的例子:

[Flags] // indicates bitwise operations occur on this enum
public enum DaysOfWeek : byte // byte type to limit size
{
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64,
    Weekend = Sunday | Saturday,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}

This enum can then be consumed by using bitwise math. See the below example for some applications.

然后可以使用位数学来使用这个枚举。有关一些应用程序,请参见下面的示例。

public static class DaysOfWeekEvaluator
{
    public static bool IsWeekends(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekend) == DaysOfWeek.Weekend;
    }

    public static bool IsAllWeekdays(DaysOfWeek days)
    {
        return (days & DaysOfWeek.Weekdays) == DaysOfWeek.Weekdays;
    }

    public static bool HasWeekdays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekdays)) > 0;
    }

    public static bool HasWeekendDays(DaysOfWeek days)
    {
        return ((int) (days & DaysOfWeek.Weekend)) > 0;
    }
}

#2


4  

There is no implicit cast because the enum does not have to use int as the underlying type. If your enum used a uint as the underlying type, for instance, there is no implicit cast from uint to int.

没有隐含的转换,因为枚举不需要使用int作为底层类型。如果枚举使用uint作为底层类型,例如,没有从uint到int的隐式转换。

#3


4  

The c# enum is useless.

c# enum是没用的。

You can avoid casting from your type AND constrain the values that can be explicitly cast to your type by making a sealed class, and providing implicit/explicit conversion operators.

您可以避免使用类型转换,并通过创建一个密封的类,并提供隐式/显式转换操作符来约束可以显式转换为类型的值。

  • Provide an implicit operator for converting from your type to a generic int so you don't have to cast.
  • 提供一个隐式运算符,用于将类型转换为通用int类型,这样就不需要强制转换了。
  • Provide an explicit operator for converting from an int to your type, which throws an error if the integer fails to meet the constraint, such as (int x) => (x >= 0 && x <= 2).
  • 提供一个显式运算符,用于将int类型转换为您的类型,如果该整数不能满足约束,则会抛出一个错误,例如(int x) => (x >= 0 & x <= 2)。

If using this technique, create a generic immutable base class such as ConstrainedNumber<T>, which has a constructor that accepts a T value and delegate for the constraint: delegate bool NumberConstraint<T>(T value). The constructor should run the value through the constraint delegate, and throw an exception if it fails to meet the constraint. The base class should also take care of the implicit conversion operation to T, and should handle equality by overloading object.Equals(object) and object.GetHashCode(), defining == and != operators for the type ConstrainedNumber<T>, and implementing IEquatable<T> and IEquatable<ConstrainedNumber<T>>. I also recommend defining an copy constructor for the base class, and all derived types. Cloning can then be implemented cleanly in the base class by retrieving the copy constructor via reflection, but this is entirely optional. You can figure out the ConstrainedNumber<T> implementation yourself, unless I've already posted it on * somewhere.

如果使用此技术,则创建一个通用的不可变基类,如ConstrainedNumber ,该基类具有一个构造函数,该构造函数接受T值并为约束委托:委托bool number约束 (T值)。构造函数应该通过约束委托运行该值,如果该值不满足约束,则抛出异常。基类还应该处理到T的隐式转换操作,并且应该通过重载object. equals (object)和object. gethashcode()来处理等式,为类型ConstrainedNumber 定义=和!=操作符,并实现IEquatable 和IEquatable 。我还建议为基类和所有派生类型定义复制构造函数。然后可以通过反射检索复制构造函数,在基类中干净地实现克隆,但这完全是可选的。您可以自己找出ConstrainedNumber 实现,除非我已经在*上发布过。

You can provide named static readonly values in your derived ConstrainedNumber, so that you can access them just like an enum.

您可以在派生的ConstrainedNumber中提供已命名的静态只读值,以便像枚举一样访问它们。

public sealed class ReturnValue: ConstrainedNumber<int>
{
    public static readonly NumberConstraint<int> constraint = (int x) => (x >= 0 && x < 3);

    public static readonly ReturnValue Success = new ReturnValue(0);
    public static readonly ReturnValue FailReason1 = new ReturnValue(1);
    public static readonly ReturnValue FailReason2 = new ReturnValue(2);

    private ReturnValue( int value ): base( value, constraint ) {}
    private ReturnValue( ReturnValue original ): base (original) {} //may be used to support IClonable implementation in base class
    public static explicit operator ReturnValue( int value )
    {
        switch(value) //switching to return an existing instance is more efficient than creating a new one and re-checking the constraint when there is a limited number of allowed values; if the constraint was more generic, such as an even number, then you would instead return a new instance here, and make your constructors public.
        {
            case 0: return Success;
            case 1: return FailReason1;
            case 2: return FailReason2;
        }
        throw new ArgumentException( "Value fails to meet the constraint defined for " + typeof(ReturnValue).FullName + ".", "value" );
    }

}

You could use this technique for any constraint. For example, a class called EvenNumber may have a constraint that returns true if the given number is even. In that case, you'd just make your constructors public, and simplify your static conversion operator to just return a new EvenNumber, instead of switching to return one of the limited existing instances.

您可以对任何约束使用这种技术。例如,一个名为EvenNumber的类可能有一个约束,如果给定的数字是偶数,则返回true。在这种情况下,您只需将构造函数公开,并将静态转换操作简化为只返回一个新的偶数,而不是切换到返回一个有限的现有实例。

It could be used like this:

可以这样使用:

EvenNumber x = (EvenNumber)2;
EvenNumber y = (EvenNumber)3; //throws exception "Value fails to meet the constraint defined for {namespace}.EvenNumber."  A c# enum would stupidly allow such a cast, creating an invalid EvenNumber, breaking the object-oriented model
int z = x; //implicit conversion, no cast necessary;

#4


2  

Enums and ints are simply not implicitly castable as per the spec (except for the literal 0, which is allowed for comparison tests / assignments / etc). The explicit cast is all that is needed, though.

枚举和int只是不能根据规范隐式地浇铸(除了文字0,允许进行比较测试/赋值/等等)。然而,明确的演员阵容才是最需要的。

#5


0  

No, you can't avoid casting; as to why there's no implicit conversion, I don't know, but there's not.

不,你不能避免铸造;至于为什么没有隐式转换,我不知道,但没有。

#6


0  

Strangely enough, this is not specific to the .NET Framework, but just to C#. As the other commenters have already pointed out, in C# this is basically a specification of the language. The same is not true in VB.NET.

奇怪的是,这不是特定于。net框架,而是c#。正如其他评论者已经指出的,在c#中,这基本上是语言的一个规范。在VB.NET中,情况并非如此。

Check out the MSDN reference page for Enums in VB.NET. Note that you can specify the data type of an enumeration at Enum declaration time.

请查看VB.NET中Enums的MSDN参考页面。注意,您可以在Enum声明时指定枚举的数据类型。

That means, if you really don't want to litter your code with casts to (int), you could write your enumeration in VB.NET, declare it as an integer, then use that Enum from C#.

这意味着,如果您真的不想在代码中添加强制类型转换(int),您可以用VB编写枚举。NET,将它声明为一个整数,然后使用c#中的Enum。

Remember how they told us computers would make our lives so much simpler? :)

还记得他们告诉我们电脑会让我们的生活变得更简单吗?:)

#7


0  

You can ascribe this behaviour to the basic intention behind creating Enumerations... to create a set of named constants that can only have specified (or default) values depending on the underlying type.

您可以将这种行为归因于创建枚举的基本意图……要创建一组命名的常量,这些常量只能根据底层类型指定(或默认)值。

There are two separate issues to consider, as related to your question:

关于你的问题,有两个不同的问题需要考虑:

  1. An Enum value cannot be treated as an int by default because then you would be able to provide any integer and there would be no compile time check to validate that the provided integer does in fact exist as a value in the Enumeration.

    枚举值在默认情况下不能被视为int,因为这样您就可以提供任何整数,并且没有编译时检查来验证所提供的整数实际上是否作为枚举中的值存在。

  2. Casting becomes necessary since you are trying to convert from the governing type (of type YourCustomEnum which derives from the System.Enum class) to the underlying type, i.e., int or byte, etc.

    由于您试图从管理类型转换(从系统派生的自定义枚举类型),因此需要进行转换。枚举类型)到底层类型,即, int或byte,等等。

#8


-2  

How about using static Members of a Class?

如何使用类的静态成员?

//enum DocInfos { DocName, DocNumber, DocVersion};
public class DocInfos
{
    public static int DocName = 0;
    public static int DocNumer = 1;
    public static int DocVersion = 2;
}

...

            Doc = new string[DocInfos.DocVersion];
            // Treffer
            Doc[DocInfos.DocName] = TrimB(HTMLLines[lineCounter + 2])

...