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

У меня есть перечисление следующим образом. У меня также есть строка in-progress Я пытаюсь разобрать эту строку в соответствующее перечисление. Как видно из следующего теста, мы хотим проанализировать, чтобы взять строку и вернуть перечисление

    [Fact]
    public void TestParseOfEnum()
    {
        var data = "not-started";

        var parsed = EnumExtensions.GetValueFromEnumMember<CarePlan.CarePlanActivityStatus>(data);

        parsed.ShouldBe(CarePlan.CarePlanActivityStatus.NotStarted);
    }

Проблема в том, что синтаксический анализ enum try проверяет имя, что означает его сбой. Мне нужно разобрать его по этому пользовательскому атрибуту.

 CarePlan.CarePlanActivityStatus status
 Enum.TryParse("in-progress", out status)

Попытка синтаксического анализа не найдет этого, так как попытка синтаксического анализа проверяет поле имени, а не настраиваемый атрибут в этом перечислении. Так что это вернет false и не найдет мое перечисление

Я пытался понять, как я могу получить список всех полей в перечислении, а затем проверить литерал.

Это сработает, но я должен указать каждое из значений в перечислении в getfield

  var res = typeof(CarePlan.CarePlanActivityStatus)
                 .GetField(nameof(CarePlan.CarePlanActivityStatus.Cancelled))
                 .GetCustomAttribute<EnumLiteralAttribute>(false)
                 .Literal;
            

Я пробовал что-то вроде этого, но Literal на данный момент, по-видимому, не существует, поэтому это тоже не работает.

  var hold = typeof(CarePlan.CarePlanActivityStatus).GetFields().Where(a =>
            a.GetCustomAttributes<EnumLiteralAttribute>(false).Literal.Equals(data));

перечисление

Пакет nuget, который я использую для библиотеки fhir, — Hl7.Fhir.R4.

[FhirEnumeration("CarePlanActivityStatus")]
public enum CarePlanActivityStatus
{
  /// <summary>
  /// Care plan activity is planned but no action has yet been taken.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("not-started", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Not Started")] NotStarted,
  /// <summary>
  /// Appointment or other booking has occurred but activity has not yet begun.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("scheduled", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Scheduled")] Scheduled,
  /// <summary>
  /// Care plan activity has been started but is not yet complete.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("in-progress", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("In Progress")] InProgress,
  /// <summary>
  /// Care plan activity was started but has temporarily ceased with an expectation of resumption at a future time.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("on-hold", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("On Hold")] OnHold,
  /// <summary>
  /// Care plan activity has been completed (more or less) as planned.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("completed", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Completed")] Completed,
  /// <summary>
  /// The planned care plan activity has been withdrawn.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("cancelled", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Cancelled")] Cancelled,
  /// <summary>
  /// The planned care plan activity has been ended prior to completion after the activity was started.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("stopped", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Stopped")] Stopped,
  /// <summary>
  /// The current state of the care plan activity is not known.  Note: This concept is not to be used for "other" - one of the listed statuses is presumed to apply, but the authoring/source system does not know which one.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("unknown", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Unknown")] Unknown,
  /// <summary>
  /// Care plan activity was entered in error and voided.
  /// (system: http://hl7.org/fhir/care-plan-activity-status)
  /// </summary>
  [EnumLiteral("entered-in-error", "http://hl7.org/fhir/care-plan-activity-status"), Hl7.Fhir.Utility.Description("Entered in Error")] EnteredInError,
}

// пример всего, что я тестировал

Пакет nuget, который я использую для библиотеки fhir, — Hl7.Fhir.R4.

public static class EnumExtensions
{
    public static T GetValueFromEnumMember<T>(string value) where T : Enum
    {
            {
                
                
                // Doesnt work as .Literal shows as red not valid.
                var hold = typeof(CarePlan.CarePlanActivityStatus).GetFields().Where(a =>
                      a.GetCustomAttributes<EnumLiteralAttribute>(false).Literal.Equals(data));
                
                
                // doesnt work as its returning the string i need it to return the enum
                var res = typeof(CarePlan.CarePlanActivityStatus)
                    .GetField(nameof(CarePlan.CarePlanActivityStatus.Cancelled))
                    .GetCustomAttribute<EnumLiteralAttribute>(false)
                    .Literal;
                
                
                

// neither of the following two work as they are just looping though and i cant find the enum they find.
                foreach (CarePlan.CarePlanActivityStatus status in (CarePlan.CarePlanActivityStatus[])Enum.GetValues(
                             typeof(CarePlan.CarePlanActivityStatus)))
                {
                }

                foreach (string i in Enum.GetNames(typeof(CarePlan.CarePlanActivityStatus)))
                {
                    // var res = typeof(CarePlan.CarePlanActivityStatus)
                    //     .GetField(nameof(CarePlan.CarePlanActivityStatus[i]))
                    //     .GetCustomAttribute<EnumLiteralAttribute>(false)
                    //     .Literal;
                    //
                    // Console.WriteLine($" {res}" );  
                    //
                    // Console.WriteLine($" {i}" );  
                }
                
            }

Нет ли способа разобрать строку в перечисление, не создавая большого беспорядка в операторах if для его проверки. В идеале мне нужно создать общий метод, так как у меня есть около 10 из этих перечислений, которые мне нужно проверить.

«Я пробовал что-то подобное, но Literal, по-видимому, на данный момент не существует» - так и должно быть. Я ожидаю, что вы сможете использовать typeof(CarePlanActivityStatus).GetFields().Select(f => (f, f.GetCustomAttribute<EnumLiteral>())) и двигаться дальше.

Jon Skeet 04.04.2022 10:34

(Я бы предложил переписать это как минимальный воспроизводимый пример без сводных тегов или атрибутов Description, вместе с примером того, что вы пробовали и где что-то пошло не так — это будет иметь гораздо лучшее отношение сигнал-шум.)

Jon Skeet 04.04.2022 10:35

это в значительной степени минимальный пример. Это перечисление, которое я использую. Я тестирую одно консольное приложение, вот и все. Перечисление взято из пакета Hl7.Fhir.R4 nuget.

DaImTo 04.04.2022 10:44

Мне пришлось немного помассировать его, например, определить свой собственный класс EnumLiteralAttribute и удалить атрибуты Description, чтобы построить его на Sharplab.io. Это означает, что это не минимальный воспроизводимый пример: я должен просто скопировать и вставить код из вашего вопроса, например. Sharplab и сразу увидите свою проблему. Как пользователь с 86 000 повторений, вы также должны знать, что «это тоже не работает» — это плохое объяснение ошибки: что вы видите, что заставляет вас думать, что это не удается? Исключение? Что-то другое? Я думаю, что также есть ошибка, которая GetCustomAttributes должна быть GetCustomAttribute

canton7 04.04.2022 10:46

@canton7 вот так. Это тот же код, который у меня был раньше. просто все в классе надеются, что это поможет. Исключений нет, он просто не возвращает перечисление

DaImTo 04.04.2022 10:59

Спасибо. "Не работает, поскольку .Literal показывает, что красный недействителен." -- это потому, что вы звоните GetCustomAttributes, а не GetCustomAttribute, смотрите мой ответ. «не работает, так как возвращает строку, мне нужно, чтобы она возвращала перечисление» — вы обращаетесь к свойству Literal перечисления, поэтому вы получаете строку. Чтобы получить значение перечисления, вызовите GetValue(null) в FieldInfo. Я обновил свой ответ, чтобы объяснить это.

canton7 04.04.2022 11:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
6
72
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

public static class EnumExtensions
{
    public static T GetValueFromEnumMember<T>(string value) where T: Enum
    {
        var type = typeof(T);
        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(EnumMemberAttribute)) as EnumMemberAttribute;
            if (attribute != null)
            {
                if (attribute.Value == value)
                    return (T)field.GetValue(null);
            }
            else
            {
                if (field.Name == value)
                    return (T)field.GetValue(null);
            }
        }
        throw new ArgumentException($"unknow value: {value}");
    }
}

и используйте его так:

EnumExtensions.GetValueFromEnumMember<CarePlanActivityStatus>(stringValueToTest)

он возвращает значение Enum

Ты гений, я боролся с этим целый час. Я внес одно изменение в ваш код для проверки на Litteral, и оно работает.

DaImTo 04.04.2022 10:52

@DalmTo Обратите внимание, что это не сработает, если у вас есть несколько атрибутов в одном поле, как указано в вашем комментарии к моему ответу.

canton7 04.04.2022 11:10

I tried something like this but Literal doesn't exist at this point apparently so this fails as well.

Одна ошибка в вашем вопросе заключается в том, что вы звоните GetCustomAttributes, а не GetCustomAttribute. GetCustomAttributes возвращает IEnumerable<EnumLiteralAttribute>, у которого, конечно, не будет свойства Literal. GetCustomAttribute возвращает один EnumLiteralAttribute (или null), и вы можете получить доступ к его свойству Literal.

Это приведет к появлению сообщения об ошибке:

error CS1061: 'IEnumerable<EnumLiteralAttribute>' does not contain a definition for 'Literal' and no accessible extension method 'Literal' accepting a first argument of type 'IEnumerable<EnumLiteralAttribute>' could be found (are you missing a using directive or an assembly reference?)

Другая проблема заключается в том, что это не удастся с NullReferenceException. Если вы посмотрите, что на самом деле возвращает GetFields(), первое поле, которое он возвращает, это value__, у которого нет никаких атрибутов. Таким образом, ваш GetCustomAttribute<EnumLiteralAttribute>() возвращает null, а доступ к его элементу Literal завершается с ошибкой NullReferenceException.

Если вы исключите это поле из своего теста или просто убедитесь, что вы можете GetCustomAttribute вернуть null, все будет работать нормально.

Например.:

var hold = typeof(CarePlanActivityStatus).GetFields()
    .Where(a => a.GetCustomAttribute<EnumLiteralAttribute>(false)?.Literal == data);

Посмотреть на SharpLab.

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

var hold = typeof(CarePlanActivityStatus).GetFields()
    .FirstOrDefault(a => a.GetCustomAttribute<EnumLiteralAttribute>(false)?.Literal == data)
    ?.GetValue(null);

Посмотреть на SharpLab.

Судя по комментариям, у вас может быть несколько атрибутов в одном поле, и вы хотите увидеть, соответствует ли какой-либо из их Literal атрибутов data.

В этом случае вы захотите использовать GetCustomAttributes, чтобы получить все атрибуты, но затем вы захотите просмотреть их и посмотреть, соответствует ли атрибут Literal в Любые того, что вы хотите. Проще всего это сделать с помощью Linq Any:

string data = "cancelled";
var hold = typeof(CarePlanActivityStatus).GetFields()
    .FirstOrDefault(a => a.GetCustomAttributes<EnumLiteralAttribute>(false)
        .Any(x => x.Literal == data))
    ?.GetValue(null);
hold.Dump();

Посмотреть на SharpLab.

проблема была в том, что ?.Literal даже недействителен. Я не мог использовать это поле.

DaImTo 04.04.2022 10:53

@DaImTo Тогда я думаю, проблема была в том, что ты звонил GetCustomAttributes, а не GetCustomAttribute? Сообщение об ошибке в вашем вопросе прояснит это. Обновил мой ответ.

canton7 04.04.2022 10:53

Проблема в том, что в перечислении есть более одного атрибута, поэтому я использовал GetCustomAttributes, чтобы проверить их все. Интересно, если бы добавление where помогло бы.

DaImTo 04.04.2022 11:07

Код в вашем вопросе не имеет нескольких атрибутов для каждого члена? Если это может случиться, вам следует использовать GetCustomAttributes, но вы должны проверить, имеет ли Любые из них свойство Literal с правильным значением. т.е. fieldInfo.GetCustomAttributes<EnumLiteralAttribute>().Any(x => x.Literal == data). Я обновил свой ответ.

canton7 04.04.2022 11:08

GetCustomAttributes<EnumLiteralAttribute> возвращал список из двух элементов. Я очень ценю, что вы нашли время, но другой ответ решил проблему. Теперь он работает отлично.

DaImTo 04.04.2022 11:17

@DaImTo Другой ответ не будет обрабатывать два атрибута одного и того же члена, что, как вы только что сказали, вызывает беспокойство. Я пытался объяснить Зачем, что вы получаете ошибки, которые у вас были, вместо того, чтобы просто дать вам (слегка дрянной) блок кода без объяснения причин. По крайней мере, я буду благодарен, если отвечу на ваш вопрос :)

canton7 04.04.2022 11:20

вы можете использовать это:

var enumType = typeof(CarePlanActivityStatus);
FieldInfo field = enumType.GetField(nameof(CarePlanActivityStatus.Cancelled));
var enumLiteralAttribute = field.GetCustomAttribute<EnumLiteralAttribute>(false);
WriteLine(enumLiteralAttribute.Name); // canceled
WriteLine(enumLiteralAttribute.Url); // http://hl7.org/fhir/care-plan-activity-status

Это похоже на немного более многословную версию первого фрагмента кода в вопросе?

canton7 04.04.2022 10:49

Привет, этот код имеет успешный результат: WriteLine(enumLiteralAttribute.Name); // canceled WriteLine(enumLiteralAttribute.Url); // http://hl7.org/fhir/care-plan-activity-status

M Safari 04.04.2022 11:02

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