Can we define implicit conversions of enums in c#?

Viewed 77473

Is it possible to define an implicit conversion of enums in c#?

something that could achieve this?

public enum MyEnum
{
    one = 1, two = 2
}

MyEnum number = MyEnum.one;
long i = number;

If not, why not?

15 Answers

You can't do implict conversions (except for zero), and you can't write your own instance methods - however, you can probably write your own extension methods:

public enum MyEnum { A, B, C }
public static class MyEnumExt
{
    public static int Value(this MyEnum foo) { return (int)foo; }
    static void Main()
    {
        MyEnum val = MyEnum.A;
        int i = val.Value();
    }
}

This doesn't give you a lot, though (compared to just doing an explicit cast).

One of the main times I've seen people want this is for doing [Flags] manipulation via generics - i.e. a bool IsFlagSet<T>(T value, T flag); method. Unfortunately, C# 3.0 doesn't support operators on generics, but you can get around this using things like this, which make operators fully available with generics.

You cannot declare implicit conversions on enum types, because they can't define methods. The C# implicit keyword compiles into a method starting with 'op_', and it wouldn't work in this case.

You probably could, but not for the enum (you can't add a method to it). You could add an implicit conversion to you own class to allow an enum to be converted to it,

public class MyClass {

    public static implicit operator MyClass ( MyEnum input ) {
        //...
    }
}

MyClass m = MyEnum.One;

The question would be why?

In general .Net avoids (and you should too) any implicit conversion where data can be lost.

enums are largely useless for me because of this, OP.

I end up doing pic-related all the time:

the simple solution

classic example problem is the VirtualKey set for detecting keypresses.

enum VKeys : ushort
{
a = 1,
b = 2,
c = 3
}
// the goal is to index the array using predefined constants
int[] array = new int[500];
var x = array[VKeys.VK_LSHIFT]; 

problem here is you can't index the array with the enum because it can't implicitly convert enum to ushort (even though we even based the enum on ushort)

in this specific context, enums are obsoleted by the following datastructure . . . .

public static class VKeys
{
public const ushort
a = 1,
b = 2, 
c = 3;
}

If you define the base of the enum as a long then you can perform explicit conversion. I don't know if you can use implicit conversions as enums cannot have methods defined on them.

public enum MyEnum : long
{
    one = 1,
    two = 2,
}

MyEnum number = MyEnum.one;
long i = (long)number;

Also, be aware with this that an uninitalised enumeration will default to the 0 value, or the first item - so in the situation above it would probably be best to define zero = 0 as well.

I created this utility to help me convert an Enum to PrimitiveEnum and PrimitiveEnum to byte, sbyte, short, ushort, int, uint, long, or ulong.

So, this technically converts any enum to any its primitive value.

public enum MyEnum
{
    one = 1, two = 2
}

PrimitiveEnum number = MyEnum.one;
long i = number;

See commit at https://github.com/McKabue/McKabue.Extentions.Utility/blob/master/src/McKabue.Extentions.Utility/Enums/PrimitiveEnum.cs

using System;

namespace McKabue.Extentions.Utility.Enums
{
    /// <summary>
    /// <see href="https://stackoverflow.com/q/261663/3563013">
    /// Can we define implicit conversions of enums in c#?
    /// </see>
    /// </summary>
    public struct PrimitiveEnum
    {
        private Enum _enum;

        public PrimitiveEnum(Enum _enum)
        {
            this._enum = _enum;
        }

        public Enum Enum => _enum;


        public static implicit operator PrimitiveEnum(Enum _enum)
        {
            return new PrimitiveEnum(_enum);
        }

        public static implicit operator Enum(PrimitiveEnum primitiveEnum)
        {
            return primitiveEnum.Enum;
        }

        public static implicit operator byte(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToByte(primitiveEnum.Enum);
        }

        public static implicit operator sbyte(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToSByte(primitiveEnum.Enum);
        }

        public static implicit operator short(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt16(primitiveEnum.Enum);
        }

        public static implicit operator ushort(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt16(primitiveEnum.Enum);
        }

        public static implicit operator int(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt32(primitiveEnum.Enum);
        }

        public static implicit operator uint(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt32(primitiveEnum.Enum);
        }

        public static implicit operator long(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToInt64(primitiveEnum.Enum);
        }

        public static implicit operator ulong(PrimitiveEnum primitiveEnum)
        {
            return Convert.ToUInt64(primitiveEnum.Enum);
        }
    }
}

Here's a different flavour based on adminSoftDK's answer.

/// <summary>
/// Based on https://datatracker.ietf.org/doc/html/rfc4346#appendix-A.1
/// </summary>
[DebuggerDisplay("{_value}")]
public struct HandshakeContentType
{
    #region Types
    public const byte ChangeCipher = 0x14;
    public const byte Alert = 0x15;
    public const byte Handshake = 0x16;
    public const byte ApplicationData = 0x17;
    #endregion

    byte _value;
    private HandshakeContentType(byte value)
    {
        _value = value;

        switch (_value)
        {
            case ChangeCipher:
            case Alert:
            case Handshake:
            case ApplicationData:
                break;

            default:
                throw new InvalidOperationException($"An invalid handshake content type (${value}) was provided.");
        }
    }

    #region Methods
    public static implicit operator byte(HandshakeContentType type) => type._value;
    public static implicit operator HandshakeContentType(byte b) => new HandshakeContentType(b);
    #endregion
}

This allows you to use this struct with switch statements which I think is pretty awesome.

@BatteryBackupUnit Hey this sounds like a cool solution but could you explain this part here?

Since im getting with .NET 4.7.2 an "InvalidCastException" out of this sadly :/

 _name = new Lazy<string>(
                () => EnumInstanceToNameMapping
                     .First(x => ReferenceEquals(this, x.Instance))
                     .Name);

I dont know why, i have created a derived type of the RichEnum and initialized as everything u did in the example but i getthis annyoingg exception..

Would be glad of some help to this since i like this approach alot tbh.

I don't have enough rep to add a comment, but I was inspired by the 'struct' comment here: https://stackoverflow.com/a/39141171/12135042

Here is how I did it:

public enum DaysOfWeek
{
   Sunday = 0,
   Monday = 1,
   Tuesday = 2,
   Wednesday = 3,
   Thursday = 4,
   Friday = 5,
   Saturday = 7,
}

public struct Weekends
{
   private Weekends(DaysOfWeek day){ Day = day; }
   public readonly DaysOfWeek Day;
   public static Weekends Sunday = new(DaysOfWeek.Sunday);
   public static Weekends Saturday = new(DaysOfWeek.Saturday);
   
   public static implicit operator DaysOfWeek(Weekends value) => value.Mode;

}

I feel this gets the best of both worlds here, since you get your super enum, and easily accessible structs that are statically accessibly acting as subsets of the superenum.

Introducing implicit conversions for enum types would break type safety, so I'd not recommend to do that. Why would you want to do that? The only use case for this I've seen is when you want to put the enum values into a structure with a pre-defined layout. But even then, you can use the enum type in the structure and just tell the Marshaller what he should do with this.

Related