Daniel Bencik
Daniel Bencik

Reputation: 969

Generic class for enumerations - casting issue

I want to write a generic class that accepts enumerations. Since this class is intended to implement some interfaces, the main aim is to be able to treat enumerations as other objects implementing those interfaces(e.g. for list extensions, etc). Hence, for a sample enum

public enum QEnum : int
{
    xlNoValue = 0,
    xlSomeValue = 1
}

public static class QEnumExtensions
{
    public static string toString(this QEnum xThis)
    {
        ...
    }

    public static QEnum toEnum(this string xThis)
    {
        ...
    }
}

I would like to declare a generic class such as

public class QEnumHolder<T>  where T : struct, IConvertible
{
    private T mxVal = default(T);

    public QEnumHolder()
    {
        if (!typeof(T).IsEnum) throw new NotSupportedException();
    }

    public QEnumHolder(T xVal) 
    {
        if (!typeof(T).IsEnum) throw new NotSupportedException();
        mxVal = xVal;
    }

    static public implicit operator QEnumHolder<T>(T xVal)
    {
        return new QEnumHolder<T>(xVal);
    }

    static public implicit operator T(QEnumHolder<T> xVal)
    {
        return (T)xVal.mxVal;
    }

    public string toString()
    {
        if (mxVal is QEnum) return ((QEnum)Convert.ToInt32(mxVal)).toString();     
        ...
     }

    public void fromString(string xString)
    {
        if (mxVal is QEnum)
            mxVal = (???)xString.toEnum();       // problem
    }
}

All of the enumerations that we use implement

hence the structure of toString/toEnum is pretty much given. The problem is with the last code line marked "problem". I have no idea how to tell the compiler that in this branch, the return type of toEnum() and T will be the same.

I tried to circumvent the problem by declaring mxVal as int and using Convert.ToInt32 everywhere. However, then I run into problem in the operator T where the compiler has objections against converting int to a T (the compiler can't know that T will be enum, hence I can't use none of the "int to enum conversion" discussions here on SO).

Upvotes: 2

Views: 133

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205899

A better design would be to use some naming convention, put all your enum extension methods in one and the same static class, and bind these functions inside your holder class static constructor. Something like this:

public static partial class MyEnumExtensions
{
    public static MyEnumHolder<T> ToHolder<T>(this T source)
        where T : struct, IConvertible
    {
        return new MyEnumHolder<T>(source);
    }
}

public class MyEnumHolder<T> where T : struct, IConvertible
{
    static readonly Func<T, string> toStringFunc;
    static readonly Func<string, T> toEnumFunc;
    static MyEnumHolder()
    {
        if (!typeof(T).IsEnum) throw new NotSupportedException();
        // Use your naming conventions
        var name = typeof(T).Name;
        toStringFunc = (Func<T, string>)Delegate.CreateDelegate(typeof(Func<T, string>),
            typeof(MyEnumExtensions).GetMethod("toString", new[] { typeof(T) }));
        toEnumFunc = (Func<string, T>)Delegate.CreateDelegate(typeof(Func<string, T>),
            typeof(MyEnumExtensions).GetMethod("to" + name, new[] { typeof(string) }));
    }

    private T value;
    public MyEnumHolder() { value = default(T); }
    public MyEnumHolder(T value) { this.value = value; }
    static public implicit operator MyEnumHolder<T>(T x) { return new MyEnumHolder<T>(x); }
    static public implicit operator T(MyEnumHolder<T> x) { return x.value; }
    public string toString()
    {
        return toStringFunc(value);
    }
    public void fromString(string xString)
    {
        value = toEnumFunc(xString);
    }
}

Sample enum definitions (could be in separate files, but must be inside the same project):

public enum MyEnumA { A1, A2, A3 }
partial class MyEnumExtensions
{
    public static string toString(this MyEnumA x)
    {
        //...
        return x.ToString();
    }
    public static MyEnumA toMyEnumA(this string x)
    {
        //...
        return (MyEnumA)Enum.Parse(typeof(MyEnumA), x);
    }
}

and

public enum MyEnumB { B1, B2, B3 }
partial class MyEnumExtensions
{
    public static string toString(this MyEnumB x)
    {
        //...
        return x.ToString();
    }
    public static MyEnumB toMyEnumB(this string x)
    {
        //...
        return (MyEnumB)Enum.Parse(typeof(MyEnumB), x);
    }
}

test:

var a = MyEnumA.A1.ToHolder();
var sA = a.toString();
a.fromString("A2");
var b = MyEnumB.B2.ToHolder();
var sB = b.toString();
b.fromString("B1");

Upvotes: 1

Michael Kim
Michael Kim

Reputation: 771

mxVal = (T)(object)xString.toEnum();

Upvotes: 1

Related Questions