У меня есть перечисление следующим образом. У меня также есть строка 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 из этих перечислений, которые мне нужно проверить.
(Я бы предложил переписать это как минимальный воспроизводимый пример без сводных тегов или атрибутов Description, вместе с примером того, что вы пробовали и где что-то пошло не так — это будет иметь гораздо лучшее отношение сигнал-шум.)
это в значительной степени минимальный пример. Это перечисление, которое я использую. Я тестирую одно консольное приложение, вот и все. Перечисление взято из пакета Hl7.Fhir.R4 nuget.
Мне пришлось немного помассировать его, например, определить свой собственный класс EnumLiteralAttribute
и удалить атрибуты Description
, чтобы построить его на Sharplab.io. Это означает, что это не минимальный воспроизводимый пример: я должен просто скопировать и вставить код из вашего вопроса, например. Sharplab и сразу увидите свою проблему. Как пользователь с 86 000 повторений, вы также должны знать, что «это тоже не работает» — это плохое объяснение ошибки: что вы видите, что заставляет вас думать, что это не удается? Исключение? Что-то другое? Я думаю, что также есть ошибка, которая GetCustomAttributes
должна быть GetCustomAttribute
@canton7 вот так. Это тот же код, который у меня был раньше. просто все в классе надеются, что это поможет. Исключений нет, он просто не возвращает перечисление
Спасибо. "Не работает, поскольку .Literal показывает, что красный недействителен." -- это потому, что вы звоните GetCustomAttributes
, а не GetCustomAttribute
, смотрите мой ответ. «не работает, так как возвращает строку, мне нужно, чтобы она возвращала перечисление» — вы обращаетесь к свойству Literal
перечисления, поэтому вы получаете строку. Чтобы получить значение перечисления, вызовите GetValue(null)
в FieldInfo
. Я обновил свой ответ, чтобы объяснить это.
Вы можете попробовать с помощью метода расширения прочитать пользовательский атрибут из 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, и оно работает.
@DalmTo Обратите внимание, что это не сработает, если у вас есть несколько атрибутов в одном поле, как указано в вашем комментарии к моему ответу.
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);
Если затем вы хотите получить доступ к перечислению с этим атрибутом, вызовите метод FieldInfo
GetValue
:
var hold = typeof(CarePlanActivityStatus).GetFields()
.FirstOrDefault(a => a.GetCustomAttribute<EnumLiteralAttribute>(false)?.Literal == data)
?.GetValue(null);
Судя по комментариям, у вас может быть несколько атрибутов в одном поле, и вы хотите увидеть, соответствует ли какой-либо из их 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();
проблема была в том, что ?.Literal даже недействителен. Я не мог использовать это поле.
@DaImTo Тогда я думаю, проблема была в том, что ты звонил GetCustomAttributes
, а не GetCustomAttribute
? Сообщение об ошибке в вашем вопросе прояснит это. Обновил мой ответ.
Проблема в том, что в перечислении есть более одного атрибута, поэтому я использовал GetCustomAttributes, чтобы проверить их все. Интересно, если бы добавление where помогло бы.
Код в вашем вопросе не имеет нескольких атрибутов для каждого члена? Если это может случиться, вам следует использовать GetCustomAttributes
, но вы должны проверить, имеет ли Любые из них свойство Literal
с правильным значением. т.е. fieldInfo.GetCustomAttributes<EnumLiteralAttribute>().Any(x => x.Literal == data)
. Я обновил свой ответ.
GetCustomAttributes<EnumLiteralAttribute> возвращал список из двух элементов. Я очень ценю, что вы нашли время, но другой ответ решил проблему. Теперь он работает отлично.
@DaImTo Другой ответ не будет обрабатывать два атрибута одного и того же члена, что, как вы только что сказали, вызывает беспокойство. Я пытался объяснить Зачем, что вы получаете ошибки, которые у вас были, вместо того, чтобы просто дать вам (слегка дрянной) блок кода без объяснения причин. По крайней мере, я буду благодарен, если отвечу на ваш вопрос :)
вы можете использовать это:
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
Это похоже на немного более многословную версию первого фрагмента кода в вопросе?
Привет, этот код имеет успешный результат: WriteLine(enumLiteralAttribute.Name); // canceled WriteLine(enumLiteralAttribute.Url); // http://hl7.org/fhir/care-plan-activity-status
«Я пробовал что-то подобное, но Literal, по-видимому, на данный момент не существует» - так и должно быть. Я ожидаю, что вы сможете использовать
typeof(CarePlanActivityStatus).GetFields().Select(f => (f, f.GetCustomAttribute<EnumLiteral>()))
и двигаться дальше.