Кстати, этот код действителен только для версии языка C#-11
(т. е.: создайте проект платформы Net 8.0 или более поздней версии для тестирования).
Не является ли проблема кода ниже ограничения нового синтаксиса C#
(что было бы печально, потому что этот вариант использования, я думаю, один из самых востребованных)?
Или я ошибаюсь в синтаксисе ?
Моя цель — использовать позже «IDynamicEnum» для типа, известного только во время выполнения (с использованием отражения). Но я не уверен, предназначен ли метод статического интерфейса для использования, как я пытаюсь сделать здесь, или он предназначен только для случая, когда T всегда известен во время компиляции. я просто играю с C# 11.
Я знаю, что могу полностью удалить интерфейс IDynamicEnum и использовать что-то вроде typeof(IDynamicEnum).MakeGenericType(the_type_is_got_by_reflection), но этот вопрос кажется интересным в любом случае.
/// Tag a class that acts as an enum class (in the java way)
public interface IDynamicEnum
{
string Name { get; }
ulong Value { get; }
static abstract IEnumerable GetAllValues();
}
///
public interface IDynamicEnum : IDynamicEnum
where T : class, IDynamicEnum
{
// A better signature: We strongly type
new static abstract IEnumerable GetAllValues();
// I would like to automatically implement the non generic method...
// To do that, intuitively i would like to write this line
// which is kind of a "return type covariance on static interface method"
// But sadly it gives error CS8926
//static IEnumerable IDynamicEnum.GetAllValues() => GetAllValues();
}
public class MyDynamicEnum : IDynamicEnum
{
public string Name { get; }
public ulong Value { get; }
public static MyDynamicEnum Foo { get; } = new MyDynamicEnum("Foo", 1);
public static MyDynamicEnum Bar { get; } = new MyDynamicEnum("Bar", 2);
public static MyDynamicEnum Baz { get; } = new MyDynamicEnum("Baz", 3);
public static IEnumerable AllValues { get; } = new[] { Foo, Bar, Baz };
protected MyDynamicEnum(string name, long value) { Name = name; Value = value; }
public static IEnumerable GetAllValues() => AllValues;
// So I could get rid of this annoying / duplicated line
static IEnumerable IDynamicEnum.GetAllValues() => AllValues;
}
РЕДАКТИРОВАТЬ: продолжайте читать, только если вам недостаточно абстрактного вопроса, приведенного выше.
Настоящее приложение предназначено для доброты о перечислении неизвестно заранее, например, для обработки всего крошечного набора значений, связанных с анализом файлов csproj/sln:
- Конфигурация («Отладка», « Release", пользовательские, определенные пользователями... нет возможности узнать их в заранее)
- Платформы («x86», x64, «Любой ЦП», последняя как много разных написаний, с пространством, с «ЦП» или «ЦП»)
- li>
Рамка: содержит множество различных значений. Стандартные (которые я хочу обработать): «net48», ..., «net8.0», а также много необычных/. редкое значение, которое я не хочу обрабатывать, например "net7.0-tvos". У них тоже много чего можно написать (с точкой или без...)
Итак… ок, имя «DynamicEnum», возможно, неправильное. но я хотел задать простой вопрос, а не вступать в дискуссию о проблеме XY.
Более того, я хочу, чтобы этот код работал как в net48, так и в net8.0, поэтому я воспользуюсь преимуществами компилятора. потому что он проверит компиляцию кода для bioth net8.0 и net4.8. Таким образом, ограничение статического метода дает мне больше вероятности, метод также существует для net48 (я не знаю, нет гарантии, но больше, чем если бы разработчик ожидал, что прочтет XML-документ...)
Мой текущий решение выглядит так:
// comment for stackoverflow: this interface looks simple / intuitive
public interface IDynamicEnum
{
/// Represent the culture invariant technical representation name
string NormalizedName { get; }
/// The value of the enum (if supported, see MaxValueRepresentation)
ulong Value { get; }
}
// comment for stackoverflow: This interface adds "behavior" allowed by new C# 11 syntax
public interface IDynamicEnum : IDynamicEnum
where T : class, IDynamicEnum
{
#if NET8_0_OR_GREATER
static abstract ulong? MaxValueRepresentation { get; }
static abstract IEnumerable GetAllValues();
// Is it not possible to write something like this (that does not cause a CS8926 error) ?
//static IEnumerable IDynamicEnum.GetAllValues() => GetAllValues();
#endif
}
// comment for stackoverflow: this interface is the helper class that works both for net48/net8.0
public static class IDynamicEnum_Extensions
{
#if NET8_0_OR_GREATER
public static IEnumerable GetAllValues()
where T: class, IDynamicEnum
{
return T.GetAllValues();
}
#else
public static IEnumerable GetAllValues()
where T : class, IDynamicEnum
=> (IEnumerable)_GetAllValues(typeof(T));
#endif
public static IEnumerable GetAllValues(Type dynamicEnumType)
{
var interfaceType = typeof(IDynamicEnum).MakeGenericType(dynamicEnumType);
if (!interfaceType.IsAssignableFrom(dynamicEnumType))
throw new ArgumentException($"Type {dynamicEnumType} is not implementing {interfaceType}", nameof(dynamicEnumType));
return (IEnumerable < IDynamicEnum > )_GetAllValues(dynamicEnumType);
}
static IEnumerable _GetAllValues(Type type)
{
var m = type.GetMethod("GetAllValues", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var enumerable = m.Invoke(null, null);
return (IEnumerable)enumerable;
}
}
// comment for stackoverflow: Now a class implementing it, note there is no more "#if NET80..." line here:
[DebuggerDisplay("{" + nameof(DisplayName) + ",nq}")]
public class eConfig : IDynamicEnum
{
public static eConfig Debug { get; } = new eConfig("Debug");
public static eConfig Release { get; } = new eConfig("Release");
public string DisplayName { get; }
/// Name of platform lowercased no duplicate space, trimmed
public string NormalizedName { get; }
///
/// Open constructor to add custom / weird config as static public property in a child class.
///
protected eConfig(string displayName, string normalizedName = null)
{
DisplayName = displayName;
NormalizedName = Normalize(normalizedName ?? displayName);
_AllStandardConfigurations = _AllStandardConfigurations ?? new();
_byNormalizedNames = _byNormalizedNames ?? new();
if (null == TryGetByNormalizedName(NormalizedName))
{
_byNormalizedNames.Add(NormalizedName, this);
_AllStandardConfigurations.Add(this);
}
}
public override string ToString()
{
return DisplayName;
}
public static IReadOnlyCollection AllStandardConfigurations => _AllStandardConfigurations;
static List _AllStandardConfigurations;
static Dictionary _byNormalizedNames;
public static eConfig GetByNormalizedName(string name)
{
return TryGetByNormalizedName(name)
?? throw new TechnicalException($"{name} not recognized as a valid configuration (or not yet handled, use constructor for that!)");
}
public static eConfig TryGetByNormalizedName(string name)
{
if (_byNormalizedNames.TryGetValue(Normalize(name), out var result))
return result;
return null;
}
static string Normalize(string name)
{
while (name.Contains(" "))
name = name.Replace(" ", " ");
return name.Trim().ToLowerInvariant();
}
public static IEnumerable GetAllValues() => _byNormalizedNames.Values;
public static ulong? MaxValueRepresentation => null;
string IDynamicEnum.NormalizedName => NormalizedName;
ulong IDynamicEnum.Value => throw new NotSupportedException();
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... ntation-of