Patrick Skelton Ответов: 2

Как мне получить все значения из моего расширенного класса перечисления?


Я решил, что один из перечисления в моем проекте может быть полезно быть более умным и следовать примеру Microsoft на следующей странице:

Майкрософт Продлила Перечисление

Это сокращенная версия моего кода:

public abstract class Enumeration : IComparable
{
    protected Enumeration() {}

    protected Enumeration( int id, string name )
    {
        Id = id;
        Name = name;
    }

    public int Id { get; }

    public string Name { get; }

    public int CompareTo( object other ) => Id.CompareTo( ((Enumeration)other).Id );

    public override string ToString() => Name;

    public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
    {
        Type type = typeof( T );
        FieldInfo[] fields = type.GetTypeInfo().GetFields( BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly );
        foreach( var info in fields )
        {
            var instance = new T();
            var locatedValue = info.GetValue( instance ) as T;
            if( locatedValue != null )
                yield return locatedValue;
        }
    }
    
    public override bool Equals(object obj)
    {
        if( !( obj is Enumeration otherValue ) ) return false;
        var typeMatches = GetType().Equals(obj.GetType());
        var valueMatches = Id.Equals(otherValue.Id);
        return typeMatches && valueMatches;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}


public class Fruit : Enumeration
{
    public static Fruit Apple = new AppleFruitType();
    public static Fruit Banana = new BananaFruitType();
    public static Fruit Orange = new OrangeFruitType();

    protected Fruit( int id, string name ) : base( id, name ) { }

    private class AppleFruitType : Fruit
    {
        public AppleFruitType() : base( 0, "Apple" ) {}
    }

    private class BananaFruitType : Fruit
    {
        public BananaFruitType() : base( 1, "Banana" ) {}
    }

    private class OrangeFruitType : Fruit
    {
        public OrangeFruitType() : base( 2, "Orange" ) {}
    }
}


public class MainApplication
{
    public void SomeCode()
    {
        // None of these three lines will compile.
        List<Fruit> AllFruits1 = Fruit.GetAll();
        List<Fruit> AllFruits2 = Fruit.GetAll<Fruit>();
        List<Fruit> AllFruits3 = Fruit.GetAll<Enumeration>();
    	List<Fruit> AllFruits4 = Fruit.GetAll().ToList<Fruit>;
    	List<Fruit> AllFruits5 = Fruit.GetAll<Fruit>().ToList<Fruit>;
    	List<Fruit> AllFruits6 = Fruit.GetAll<Enumeration>().ToList<Fruit>;
    }
}


Хотя информация компилятора о том, почему эти шесть строк не работают, кажется ясной, я не могу понять, что мне нужно написать, чтобы это сработало.

Может ли кто-нибудь объяснить эту очевидную вопиющую дыру в моих знаниях языка?

Что я уже пробовал:

Примеры приведены в приведенном выше коде.

2 Ответов

Рейтинг:
14

willifritz

Fruit должен иметь конструктор без параметров.
Добавь

public Fruit()
{
}

чтобы плод ваш класс и
var AllFruits2 = Fruit.GetAll<Fruit>();

должен работать.


Patrick Skelton

Это действительно все, что было нужно. компилятор сказал мне об этом, но я не добавил его, потому что глупо подумал: "если ему нужен публичный конструктор без параметров, то пример кода Microsoft наверняка будет иметь его."

Теперь все следующее компилируется нормально:

var AllFruits1 = фрукты.GetAll<fruit>();
IEnumerable<fruit> AllFruits2 = фрукты.GetAll<fruit>();
Список<фрукты&ГТ; AllFruits3 = фрукты.Способность&ЛТ;фрукты и GT;().Список();

Спасибо!

Рейтинг:
0

Keviniano Gayo

Чтобы получить определенный тип, например фрукты, вы можете сделать это:

IEnumerable<Fruit> AllFruits1 = Enumeration.GetAll<Fruit>();


Если у вас есть другой расширенный тип перечисления, например CardType:

public abstract class CardType : Enumeration
...


IEnumerable<CardType> AllCardType1 = Enumeration.GetAll<CardType>();
IEnumerable<Fruit> AllFruits1 = Enumeration.GetAll<Fruit>();


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


Patrick Skelton

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

Keviniano Gayo

Без проблем. Кстати, Public parameter-less не требуется, если только вы не собираетесь создавать экземпляр класса без параметров. Хотя компилятор добавит конструктор по умолчанию(без параметров) в фоновом режиме, если вы явно не объявили его, если в объявлении класса нет переопределения конструктора.

Patrick Skelton

МММ... Я не могу заставить его работать без явного добавления открытого конструктора без параметров. Разве объявление защищенного конструктора с параметрами не удаляет конструктор по умолчанию?

Keviniano Gayo

Да, он удалил конструктор без параметров по умолчанию

Patrick Skelton

Так что мне не нужно возвращать его обратно, чтобы получить список всех значений перечисления, или есть лучший способ?

Keviniano Gayo

Таким образом, как разработан класс перечисления, вам не нужно добавлять конструктор по умолчанию. Вам не нужно вызывать метод Fruit напрямую, например GetAll, потому что он используется как тип (для идентификации типа перечисления).
Этот код в вашем примере: List<fruit> AllFruits2 = Fruit.GetAll<fruit>(); не имеет никакого смысла, потому что вы всегда получите фруктовый тип, так как вы "получаете" все типы из самого "фрукта".
Вот как вы вызываете, чтобы получить определенный тип перечислений:
IEnumerable<cardtype> AllCardType1 = перечисление.GetAll<cardtype>();
IEnumerable<fruit> AllFruits1 = перечисление.GetAll<fruit>();

Или проверка перечисления:
Перечисление cardCurrent = CardType.Amex;

если (cardCurrent - это CardType)
{
инт равны = cardCurrent.Метод Compareto(CardType.Мастеркард);
логическое условие выполняется = cardCurrent == CardType.Виза;
}

Patrick Skelton

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

Keviniano Gayo

такого рода низкоуровневые вещи, связанные с памятью, в чем-то мне не очень хороши. Но я знаю, что использование IEnumerable в качестве переменной или для передачи по кругу эффективно по сравнению с List, потому что IEnumerable имеет меньше методов и базовый класс List. Отображение пользовательского интерфейса в приложении LOB не так интенсивно, как в видеоиграх. IMO, в LOB-приложениях удобство использования и интуитивно понятный пользовательский интерфейс важны, тогда вы можете позже улучшить его производительность..

Patrick Skelton

Итак, я думаю, что короткий ответ: да, я параноик! :)