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

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

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

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

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

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

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


person kaem    schedule 23.06.2009    source источник
comment
Возможно, было бы неплохо перепроверить свои предположения — зачем вам это нужно? Попробуйте перефразировать вопрос, чтобы включить то, что вы на самом деле пытаетесь сделать, вместо того, чтобы сосредоточиться на том, как вы подходите к проблеме. :)   -  person Rytmis    schedule 23.06.2009
comment
На самом деле, для чего вы хотите его использовать? Наличие разных реализаций одного и того же перечисления кажется противоположным тому, для чего предназначено перечисление...   -  person Guffa    schedule 23.06.2009
comment
@Groo: нет, это добавление значений в перечисление, это абстрактное перечисление, которое имеет значения, специфичные для объекта, но реализует согласованный интерфейс, поэтому его члены могут передаваться. Ответ Марка - лучшее, что я видел.   -  person Michael Blackburn    schedule 13.09.2011


Ответы (10)


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

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");
}
person Marc Gravell    schedule 23.06.2009
comment
ну, это лучшее возможное решение, я не могу использовать только одну вещь: абстрактный общедоступный класс DB‹T›, где T: Base‹T.Enum›, Interf, new(). Я понимаю, почему тогда я должен использовать статические целые числа, а не перечисления. - person kaem; 23.06.2009
comment
Не существует такой вещи, как абстрактное перечисление (которое может иметь разные реализации в подклассах), но дженерики могут быть вариантом: - НЕПРАВИЛЬНО!!! System.Enum — абстрактная база для всех перечислений. - person Simon Bridge; 24.05.2013
comment
@SimonBridge, пожалуйста, ознакомьтесь с контекстом вопроса - person Marc Gravell; 24.05.2013

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

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

    public enum WebControlTextBoxProperties { }

    public enum WebControlLabelProperties { }

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

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

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

person Rachel    schedule 19.10.2010
comment
Для меня вопрос, почему что-то нужно, — это способ запросить дополнительную информацию. Очень часто ответ на вопрос: Это невозможно. но, выяснив, почему необходимо решение, можно найти дополнительные варианты или направление. - person Trisped; 28.04.2012
comment
Споткнулся, конечно, но немногие выходят и говорят, черт возьми, язык X не может этого сделать; вот близкая альтернатива. Вместо этого они перекладывают вину на намерения программиста, что предполагает, что программист нарушил идеальную доктрину, созданную разработчиками языка. Это чепуха. Независимо от того, что вы написали лучший абзац 2010 года, Рэйчел. - person erisco; 25.05.2013

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

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

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

Джеймс.

person James    schedule 23.06.2009
comment
Проблема в том, что не я создаю дочерние классы, а некоторые пользователи, и я не хочу, чтобы они изменяли основной класс, добавляя значения в это перечисление. - person kaem; 23.06.2009
comment
хм, и у меня есть предупреждение, если я использую это решение, но это возможно сделать: new public enum TestEnum { value1, value2, value3 } hmmmm - person kaem; 23.06.2009
comment
Да, чем больше я думаю о том, что ты пытаешься сделать, это невозможно. Так как при объявлении нового общедоступного перечисления TestEnum создаст отдельный тип перечисления для базового класса. Я думаю, что правильным решением было бы иметь перечисление только в базовом классе. - person James; 23.06.2009
comment
похоже, я не могу сделать что-то подобное: class Test‹T› где T : MyClass { public Foo(T.TestEnum te) ... } потому что он должен быть в интерфейсе, и я не могу объявлять типы в интерфейсе - person kaem; 23.06.2009
comment
но вы можете просто сохранить значение перечисления как целое число и вместо этого объявить это в интерфейсе? - person James; 23.06.2009

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

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

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

person Hemant    schedule 23.06.2009
comment
Из-за открытого для расширения закрытого для изменения принципа. Мы не хотим менять базовый класс, когда нам нужно новое значение в производном классе. Вместо этого мы хотим расширить перечисление базового класса новыми значениями в производных классах. - person ferit; 24.11.2017

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

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

person Groo    schedule 23.06.2009

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

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

person Jon Grant    schedule 23.06.2009

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

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

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

Конечно, документация .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

Синий

person Simon Bridge    schedule 24.05.2013
comment
Это действительно плохая идея. Enum не является enum. Каждый раз, когда вы используете Enum в качестве типа поля/переменной (например, Enum abstractEnum), вы добавляете бокс, что совершенно излишне. Утверждение, что Enum является абстракцией enum, вводит в заблуждение, так же как и утверждение, что object является абстракцией struct, вводит в заблуждение. Среда выполнения способна обращаться с ним подобным образом, но с некоторыми нетривиальными затратами. - person Marc Gravell; 24.05.2013
comment
То, что что-то немного неэффективно, не означает, что это неправильно, кроме того, я не сказал, что это хорошая идея, я просто сказал, что это работает. - person Simon Bridge; 05.09.2013
comment
В любом случае определите «нетривиальный» ... вы называете операцию упаковки «нетривиальной» в контексте повседневного приложения .NET? Если вы беспокоитесь о влиянии операций упаковки на значение перечисления на производительность, вам следует использовать неуправляемый язык программирования, такой как C++, я боюсь, что .NET просто недостаточно быстр, чтобы вы заметили разницу, это в конце концов, это управляемая среда выполнения, а НЕ система реального времени. - person Simon Bridge; 05.09.2013

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

if ( !typeof(T).IsEnum || typeof(int) != Enum.GetUnderlyingType(typeof(T)) )
{
     throw new InvalidOperationException( typeof(T).Name + " is not an enum");
}
person tom_p_zh    schedule 13.05.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;
    }
 }
person seguso    schedule 19.07.2017