Перечисление C # в интерфейсе / базовом классе?

StackOverflow https://stackoverflow.com/questions/1031620

  •  06-07-2019
  •  | 
  •  

Вопрос

у меня проблема с перечислением

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

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+ переменных

Это было полезно?

Решение

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

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

Другие советы

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

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

    public enum WebControlTextBoxProperties { }

    public enum WebControlLabelProperties { }

... и т. д.

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

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

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

class MyClass
{
    public enum TestEnum { }

    public MyClass()
    {
    }
}

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

    public MyDerivedClass()
    {
    }
}

Используемый мной класс будет иметь доступ к TestEnum.value1, TestEnum.value2, TestEnum.value3, где as MyClass будет иметь доступ только к типу.

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

Джеймс.

Нет, это невозможно сделать.

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

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

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

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

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

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

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

ТЫ МОЖЕТ АБСТРАКТНОЕ ПЕРЕЧИСЛЕНИЕ!

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

Конечно, документация .NET немного расплывчата по этому вопросу, но класс System.Enum - ЭТО абстракция 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

Синий

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

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

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

в родительском классе объявите поле как 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;
    }
 }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top