Перечисление C # в интерфейсе / базовом классе? - PullRequest
12 голосов
/ 23 июня 2009

У меня проблема с enum

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

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

и после создания различных перечислений в некоторых родительских классах

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

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

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

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;
      // ...
   }
}

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

Ответы [ 10 ]

27 голосов
/ 19 октября 2010

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

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

    public enum WebControlTextBoxProperties { }

    public enum WebControlLabelProperties { }

... и т. Д.

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

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

25 голосов
/ 23 июня 2009

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

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");
}
3 голосов
/ 23 июня 2009

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

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 будет иметь доступ только к типу.

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

Джеймс.

1 голос
/ 23 июня 2009

Нет, это не может быть сделано.

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

0 голосов
/ 19 июля 2017

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

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

protected int state;

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

Тогда, кто переопределяет класс, может сделать это:

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;
    }
 }
0 голосов
/ 13 мая 2017

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

if ( !typeof(T).IsEnum || typeof(int) != Enum.GetUnderlyingType(typeof(T)) )
{
     throw new InvalidOperationException( typeof(T).Name + " is not an enum");
}
0 голосов
/ 24 мая 2013

ВЫ МОЖЕТ РЕЗЮМЕ 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));

Вывод вышеуказанного кода:

9

9

синий

0 голосов
/ 20 декабря 2012

Я на работе, поэтому не могу уточнить все, но это возможно до некоторой степени. Недостатком приведенного ниже кода является невозможность объединения перечислений, таких как 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 голосов
/ 23 июня 2009

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

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

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

0 голосов
/ 23 июня 2009

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

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

...