2009-06-23 2 views
12

у меня есть проблемы с перечислениемC# enum в интерфейсе/базовом классе?

мне нужно сделать перечисление в базовом классе или интерфейсе (но пустой)

class Base 
{ 
    public enum Test; 
    // ??? 
} 

и после того, как сделать diffrent перечислений в некоторых родительских классов

class Parent1 
{ 
    public enum Test {A, B, C}; 
} 

class Parent2 
{ 
    public enum Test {J, H, K}; 
} 

и теперь у меня следующий класс с методом, когда мне нужно использовать перечисление

class Test<T> 
{ 
    public void Foo(Test enum) 
    { 
     int value = (int) enum; 
     // ... 
    } 
} 

Это какой-то способ сделать что-то подобное?

Если не я должен использовать статический Интс в каждом классе ...

class Parent1 
{ 
    public static int A = 0; 
    public static int B = 5; 
    public static int C = 7; 
} 

class Parent2 
{ 
    public static int J = 1; 
    public static int H = 3; 
    public static int K = 6; 
} 

class Test<T> 
{ 
    public void Foo(int enum) 
    { 
     int value = enum; 
     // ... 
    } 
} 

I't плохо выглядит в коде ... в некоторых классах я должен использовать ~ 20 + переменные

+2

Возможно, было бы неплохо пересмотреть ваши предположения - зачем вам это нужно? Попробуйте перефразировать вопрос, чтобы включить то, что вы на самом деле пытаетесь сделать, вместо того, чтобы сосредоточиться на том, как вы приближаетесь к проблеме. :) – Rytmis

+0

Что вы хотите использовать, на самом деле? Наличие разных реализаций одного и того же перечисления, по-видимому, противоположно тому, что предназначено для перечисления ... – Guffa

+3

@Groo: нет, это добавляет значения в перечисление, это имеет абстрактное перечисление, которое имеет объектные значения, но реализует согласованный интерфейс, поэтому его члены могут быть переданы. Ответ Марка - лучшее, что я видел. –

ответ

25

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

class Base<T> where T : struct { 
    private T value; 
    public void Foo(T value) { 
     this.value = value; 
    } 
} 
class Parent1 : Base<Parent1.Enum1> { 
    public enum Enum1 {A, B, C}; 
} 
class Parent2 : Base<Parent2.Enum2> { 
    public enum Enum2 { J, H, K }; 
} 

Единственная проблема заключается в том, что это не обеспечивает, что только перечисления годны к употреблению - вы можете сделать это во время выполнения, хотя - например, в типе инициализатора:

static Base() { 
    if (!typeof(T).IsEnum) throw new InvalidOperationException(
     typeof(T).Name + " is not an enum"); 
} 
+0

хорошо его лучшее решение, я не могу использовать только одну вещь: абстрактного общественного класса DB где T: Base , Interf, новый() я понимаю, почему я должен использовать статические Int это не Перечисления то – kaem

+0

«Там это не такая вещь, как абстрактное перечисление (которое может иметь разные реализации в подклассах), но дженерики могут быть опцией: «НЕПРАВИЛЬНО !!! System.Enum является абстрактной базой для всех перечислений. –

+0

@SimonBridge, см. Контекст вопроса –

0

Нет, нет никакого способа обеспечить что-либо статичное в интерфейсе.

Возможно, вам нужно переосмыслить свой дизайн.

0

Почему наклоняю вы определяете перечисление в базовом классе:

class Base 
{ 
    public enum Test {A, B, C, J, H, K}; 
} 

И использовать только соответствующие члены перечисления в производных классах?

+0

Из-за принципа «open for extension closed for modified». Мы не хотим менять базовый класс, когда нам нужно новое значение в производном классе. Вместо этого мы хотим расширить перечисление базового класса с новыми значениями в производных классах. – ferit

1

Нет, это невозможно.

Обратите внимание, что многие перечисления часто указывают на проблему с дизайном (например, многие конструкции коммутаторов). Проверьте эту ссылку, чтобы увидеть пример того, как реорганизовать это: Replace conditional with polymorphism.

3

Вы должны быть в состоянии объявить перечисление в базовом классе, а затем измените значения за производный класс т.е.

class MyClass 
{ 
    public enum TestEnum { } 

    public MyClass() 
    { 
    } 
} 

class MyDerivedClass 
{ 
    public enum TestEnum { value1, value2, value3 } 

    public MyDerivedClass() 
    { 
    } 
} 

MyDervied класс будет иметь доступ к TestEnum.value1, TestEnum.value2, TestEnum.value3, где MyClass будет иметь доступ только к типу.

Однако лично я не вижу преимущества этого, я бы объявил ВСЕ значения enum в базовом классе и использовал только те, которые мне нужны для каждого класса.

James.

+2

Проблема в том, что я не создаю дочерние классы, но некоторые пользователи, и я не хочу, чтобы они изменяли значения добавления основного класса в этот enum – kaem

+0

hmmm, и у меня есть warrning, если я использую это решение, но его можно сделать так: новое публичное перечисление TestEnum {value1, value2, value3} hmmmm – kaem

+0

Да, я больше думаю о том, что вы пытаетесь сделать, это невозможно. Поскольку, объявив новое публичное перечисление, TestEnum создаст отдельный тип перечисления для базового класса. Я думаю, что правильным решением было бы иметь перечисление только в базовом классе. – James

27

Удивительно, как часто я нахожу людей, спорящих о почему что-то требуется, а не отвечая задан вопрос или держать schtum - либо из них будет более полезным, чем тратить время допроса почему данный запрос был сделан в отличие от другого запроса, ответчик действительно знает ответ. Ответы на вопросы, которые не были заданы, никоим образом не помогают, хорошо, ребята ?!

Возвращаясь к теме, я сегодня утром столкнулся с этим сценарием и понял, почему было бы полезно определить Enum в интерфейсе или базовом классе, а затем переопределить это одноимённый Enum в классе, который происходит из базы или интерфейса. Одно использование для такой конструкции - объектно-реляционное сопоставление и привязка управления. Вы могли бы иметь набор Перечисления, которые описывают, какие свойства ваших производных классов Привязываемые, какие виды контроля, такие как:

public enum WebControlTextBoxProperties { } 

    public enum WebControlLabelProperties { } 

... и так далее.

Поскольку вы не знаете точно, какие свойства будут существовать для данного производного класса до тех пор, пока уместное наследование не вступит в силу, но поскольку вы также можете иметь последовательные методы, вполне допустимый дизайн, чтобы ожидать, что сможет определить, что Enum будет существовать в базе/интерфейсе, но точно определите , какие члены имеют в определенном контексте в любых производных классах.

Я действительно хотел, чтобы это было возможно на C#, так как оно находится в VB, потому что это будет действительно полезная функция.

+2

Для меня вопрос о том, почему что-то требуется, - это способ запросить дополнительную информацию. Очень часто ответ на вопрос: «Это невозможно». но, узнав, почему требуется решение, можно предоставить дополнительные варианты или направление. – Trisped

+0

Trisped, конечно, но мало кто выйдет и скажет: «Черт, язык X не может этого сделать, вот близкая альтернатива.«Вместо этого они перекладывают вину на намерения программиста, что говорит о том, что программист нарушил совершенную доктрину, созданную разработчиками языка. Это вздор. Не связанный, вы написали лучший параграф 2010 года Rachel. – erisco

0

Я работаю, поэтому не могу полностью разобраться, но это возможно. Недостатком приведенного ниже кода является то, что вы не можете связывать перечисления вместе, например TextBoxProperties и MyCustomTextBoxProperties: TextboxProperties

Вот код.

public enum Test 
    { 

    } 

    public enum ThisTest 
    { 
     MyVal1, 
     MyVal2, 
     MyVal3 
    } 

    public abstract class MyBase 
    { 
     public Test MyEnum { get; set; } 
    } 

    public class MyDerived : MyBase 
    { 
     public new ThisTest MyEnum { get; set; } 
    } 
0

ВЫ CAN абстрагировать ENUM!

Почему люди настаивают на том, чтобы претендовать на вещи невозможно без проверки чего-либо?

Конечно, документация .NET немного расплывчата по этому вопросу, но класс System.Enum - это абстракция перечисления. Вы можете использовать System.Enum как переменную, которая будет ТОЛЬКО принимать значения перечисления, и вы можете получить доступ к имени или значению типа значения перечисления через этот класс.

Например:

 // abstracted enumeration value 
     Enum abstractEnum = null; 

     // set to the Console-Color enumeration value "Blue"; 
     abstractEnum = System.ConsoleColor.Blue; 

     // the defined value of "ConsoleColor.Blue" is "9": 
     // you can get the value via the ToObject method: 
     Console.WriteLine((int)Enum.ToObject(abstractEnum.GetType(), abstractEnum)); 

     // or via the GetHashCode() method: 
     Console.WriteLine(abstractEnum.GetHashCode()); 

     // the name can also be acessed: 
     Console.WriteLine(Enum.GetName(abstractEnum.GetType(), abstractEnum)); 

Выход из кода выше:

Синий

+2

Это действительно плохая идея.« Enum' не является «перечислением». Каждый раз, когда вы используете «Enum» в качестве типа поля/переменной (например, «Enum abstractEnum»), вы добавляете бокс совершенно без необходимости. Неверно утверждать, что «Enum» является абстракцией 'enum', так же, как ошибочно утверждать, что' object' является абстракцией 'struct'. Время выполнения способно * обрабатывать * это так, но при некоторой нетривиальной стоимости. –

+1

Just ' потому что что-то немного неэффективно, это не значит, что это неправильно, кроме того, я не сказал, что это хорошая идея, я просто сказал, что это работает. –

+1

Определите «нетривиальный» в любом случае ... вы называете операцию бокса 'non тривиальным "в контексте повседневного .NE T? Если вас беспокоит влияние производительности бокс-операций на значение перечисления, тогда вы должны использовать не управляемый язык программирования, такой как C++, я боюсь, что .NET просто недостаточно быстро, чтобы вы заметили разницу, это ведь управляемая среда исполнения, а не система реального времени. –

0

Мне нравится принятый ответ @Marc Gravell. Как я стал stackoverflow-новичком, мне не разрешено комментировать.Но я хотел бы добавить, что это полезно также проверить базовый Тип перечисления - особенно если вы используете операции флага-тестирование побитового флага атрибутов и преформы ...

if (!typeof(T).IsEnum || typeof(int) != Enum.GetUnderlyingType(typeof(T))) 
{ 
    throw new InvalidOperationException(typeof(T).Name + " is not an enum"); 
} 
0

Вот решение что работает для меня:

в родительском классе, объявить поле межд, а не перечисление:

protected int state; 

, чтобы родительский класс все еще может использовать это значение в качестве межд для того, чтобы обеспечить сериализации и десериализация этого значения на диск.

Затем, который переопределяет класс может это сделать:

enum states{ 
    state1, state2 
} 

this.state = state1.toInt(); 

и может получить доступ к фактической стоимости, как это:

... 
if (this.state == states.state1.toInt()){ 
     ... 
} 
else{ 
    ... 
} 

где toInt() определяется в статическом классе следующим образом:

public static class Utils 
{ 

    public static int toInt(this state s) 
    { 
     return (int)s; 
    } 
} 
Смежные вопросы