2015-09-15 3 views
2

Я хочу написать общий класс, который принимает перечисления. Поскольку этот класс предназначен для реализации некоторых интерфейсов, основная цель состоит в том, чтобы иметь возможность обрабатывать перечисления как другие объекты, реализующие эти интерфейсы (например, для расширений списка и т. Д.). Следовательно, для образца перечислимогоОбщий класс для перечислений - проблема с литьем

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) 
    { 
     ... 
    } 
} 

Я хотел бы объявить общий класс таких, как

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 
    } 
} 

Все перечислений, которые мы используем реализовать функцию

  • ToString(), который возвращает «хорошая» строка, которая может входить в comboBoxes и т. д.
  • преобразование строки в перечисление, как указано выше

Следовательно, структура toString/toEnum в значительной степени дается. Проблема в том, что последняя строка кода помечена как «проблема». Я не знаю, как сообщить компилятору, что в этой ветке возвращаемый тип toEnum() и T будет таким же.

Я попытался обойти эту проблему, указав mxVal как int и используя Convert.ToInt32 всюду. Тем не менее, тогда я столкнулся с проблемой в operator T, где у компилятора есть возражения против преобразования int в T (компилятор не может знать, что T будет перечислять, поэтому я не могу использовать ни одно из преобразований «int to enum conversion» здесь на SO).

+0

Как правило, C# использует верблюд-футляр и d для 'ToString()' вы должны переопределить. – Bauss

+0

@Bauss: Я знаю, мне это не нравится. То же самое с Equals() и т. Д. - если вы не переопределяете его, вы получаете реализацию, которую вам не интересует, и никто не уведомляет вас. –

+0

С этим дизайном много проблем. Если ваш класс содержит код типа 'is QEnum' и' (QEnum) что-то) ', т. Е. ** конкретные ** типы, то это ** не ** общий класс. Кроме того, у вас не может быть нескольких методов расширения «ToEnum (string)», которые отличаются только возвращаемым типом. –

ответ

1

Лучше дизайн будет использовать некоторые соглашения об именах, положить все ваши методы расширения перечислений в одном и том же статический класс, и связать эти функции внутри держателя класса статический конструктор. Что-то вроде этого:

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); 
    } 
} 

определения Образец перечислений (может быть в отдельных файлах, но они должны быть внутри того же проекта):

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); 
    } 
} 

и

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); 
    } 
} 

тест:

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

Иван, большое спасибо, ваш код намного элегантнее моего. –

1
mxVal = (T)(object)xString.toEnum(); 
+0

Как я уже сказал .. «если решение тривиально». Кажется, что я работаю, я буду ждать часа для обсуждения, а затем принять это. Большое большое спасибо. –

+0

Рад помочь! Раньше у меня была эта проблема, когда я имел дело с дженериками - потребовалось некоторое время, чтобы понять это. –

Смежные вопросы