Как добавить информацию метаданных для каждого значения перечисления типа перечисления в C #? - PullRequest
1 голос
/ 04 ноября 2019

Я хотел бы иметь список значений enum с расширенными данными. База данных будет сохранять только целочисленное значение, соответствующее моему значению перечисления, но в коде я хотел бы иметь больше информации, чем просто имя перечисления.

Позвольте мне привести пример:

public enum Reason
{
    NotEnoughStock, // Valid = yes, Responsability = company, Text = There is not enough stock
    ProductNotAvailabe, // Valid = no, Responsability = company, Text = Produc is not available
    PaymentNotDone, // Valid = no, Responsability = client, Text = Payment is not done
    BlackListedClient, // Valid = no, Responsability = client, Text = Client is black listed
    NotEnoughTime // Valid = yes, Responsability = company, Text = There is not enough time
}

Как я могу это сделать? Должен ли я использовать что-то еще, кроме перечисления? Мне нравится перечисление.

Ответы [ 6 ]

3 голосов
/ 04 ноября 2019

Вы можете:

  • использовать Атрибуты (C #) ;или
  • создать вспомогательный класс, который возвращает дополнительную информацию, через что-то вроде Helper.GetExtraInfo(MyType val).

Для значения ' Text ' вы можете использовать DescriptionAttribute . Для двух других я думаю, что вы должны создать новые атрибуты.

class ValidAttribute : Attribute
{
    public bool Valid { get; private set; }

    public ValidAttribute(bool valid)
    {
        this.valid = valid;
    }
}

Получить перечисление из атрибута перечисления содержит метод расширения для чтения значений атрибутов.

2 голосов
/ 04 ноября 2019

Совместно с тимтамом и Кевином я сделал это:

void Main()
{
    Reason.NotEnoughStock.GetAttributeOfType<DescriptionAttribute>().Description;
    Reason.ProductNotAvailabe.GetAttributeOfType<ResponsabilityAttribute>().Responsability;
}

public enum Reason
{
    [Description("There is not enough stock")]
    [Valid(true)]
    [Responsability("company")]
    NotEnoughStock, 

    [Description("Produc is not available")]
    [Valid(false)]
    [Responsability("company")]
    ProductNotAvailabe,

    [Description("Payment is not done")]
    [Valid(false)]
    [Responsability("client")]
    PaymentNotDone, 

    [Description("Client is black listed")]
    [Valid(false)]
    [Responsability("client")]
    BlackListedClient, 

    [Description("There is not enough time")]
    [Valid(true)]
    [Responsability("company")]
    NotEnoughTime 
}

public static class EnumHelper
{
    /// <summary>
    /// Gets an attribute on an enum field value
    /// </summary>
    /// <typeparam name="T">The type of the attribute you want to retrieve</typeparam>
    /// <param name="enumVal">The enum value</param>
    /// <returns>The attribute of type T that exists on the enum value</returns>
    /// <example>string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;</example>
    public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
    {
        var type = enumVal.GetType();
        var memInfo = type.GetMember(enumVal.ToString());
        var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
        return (attributes.Length > 0) ? (T)attributes[0] : null;
    }
}

public class ValidAttribute : Attribute
{
    public bool Valid;
    public ValidAttribute(bool valid) { Valid = valid; }
}

public class ResponsabilityAttribute : Attribute
{
    public string Responsability;
    public ResponsabilityAttribute(string responsability) { Responsability = responsability; }
}
1 голос
/ 04 ноября 2019

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

var reason = Reason.NotEnoughStock.GetReasonInfo(ReasonData);

Образец

internal class Program
{
    public static void Main(string[] args)
    {
        var reason = ReasonConstant.Reason.NotEnoughStock.GetReasonInfo(ReasonData);
    }
}

public static class ReasonUtils
{
    public static ReasonInfo GetReasonInfo(this ReasonConstant.Reason reason, Dictionary<ReasonConstant.Reason, ReasonInfo> reasonData)
    {
        if (reasonData == null)
            return null;
        if (!reasonData.ContainsKey(reason))
            return null;
        else
            return reasonData[reason];
    }
}

public class ReasonInfo
{
    public bool IsValid { get; set; }
    public string Responsability { get; set; }
    public string Text { get; set; }
}

public static class ReasonConstant
{
    public enum Reason
    {
        NotEnoughStock, // Valid = yes, Responsability = company, Text = There is not enough stock
        ProductNotAvailabe, // Valid = no, Responsability = company, Text = Produc is not available
        PaymentNotDone, // Valid = no, Responsability = client, Text = Payment is not done
        BlackListedClient, // Valid = no, Responsability = client, Text = Client is black listed
        NotEnoughTime // Valid = yes, Responsability = company, Text = There is not enough time
    }
}
0 голосов
/ 04 ноября 2019

Нет ничего доступного для достижения вашей цели простым способом.

Вы должны вручную реализовать необходимое поведение.

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

Таким образом, имея:

Причина.cs

public enum Reason
{
  NotEnoughStock,
  ProductNotAvailabe,
  PaymentNotDone,
  BlackListedClient,
  NotEnoughTime
}

Вы можете создать это перечисление:

Responsability.cs

public enum Responsability
{
  None,
  Company,
  Client
}

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

ReasonMetaData.cs

using System.Collections.Generic;

public class ReasonMetaData
{

  static public Dictionary<Reason, ReasonMetaData> All { get; private set; }
  static ReasonMetaData()
  {
    All = new Dictionary<Reason, ReasonMetaData>();
    All.Add(
      Reason.NotEnoughStock,
      new ReasonMetaData(Responsability.Company, true, "There is not enough stock"));
    All.Add(
      Reason.ProductNotAvailabe,
      new ReasonMetaData(Responsability.Company, false, "Product is not available"));
    All.Add(
      Reason.PaymentNotDone,
      new ReasonMetaData(Responsability.Client, false, "Payment is not done"));
    All.Add(
      Reason.BlackListedClient,
      new ReasonMetaData(Responsability.Client, false, "Client is black listed"));
    All.Add(
      Reason.NotEnoughTime,
      new ReasonMetaData(Responsability.Company, true, "There is not enough time"));
  }
  static public ReasonMetaData Get(Reason reason)
  {
    if ( All.ContainsKey(reason) )
      return All[reason];
    else
      return null;
  }
  public Responsability Responsability { get; private set; }
  public bool Valid { get; private set; }
  public string Text { get; private set; }

  public override string ToString()
  {
    return $"Responsability = {Enum.GetName(typeof(Responsability), Responsability)}, " +
           $"Valid = {( Valid ? "yes" : "no" )}" +
           $"Text = {Text}";
  }

  private ReasonMetaData()
  {
  }

  private ReasonMetaData(Responsability responsability, bool valid, string text)
  {
    Responsability = responsability;
    Valid = valid;
    Text = text;
  }
}

Вы также можете создать метод расширения, предложенный @RawitasKrungkaew:

ReasonMetadataHelper.cs

static public class ReasonMetadataHelper
{
  static public ReasonMetaData GetMetaData(this Reason reason)
  {
    return ReasonMetaData.Get(reason);
  }
}

Использованиеи проверка

static void Test()
{
  Console.WriteLine("Metadata of Reason.NotEnoughStock is:");
  Console.WriteLine(Reason.NotEnoughStock.GetMetaData());
  Console.WriteLine("");
  Console.WriteLine("Metadata of Reason.ProductNotAvailabe is:");
  Console.WriteLine(ReasonMetaData.Get(Reason.ProductNotAvailabe));
  Console.WriteLine("");
  Console.WriteLine("All metadata of Reason enum are:");
  foreach ( var item in ReasonMetaData.All )
    Console.WriteLine($"  {item.Key}: {item.Value}");
}

Вывод

Metadata of Reason.NotEnoughStock is:
Responsability = Company, Valid = yes, Text = There is not enough stock

Metadata of Reason.ProductNotAvailabe is:
Responsability = Company, Valid = no, Text = Product is not available

All metadata of Reason enum are:
  NotEnoughStock: Responsability = Company, Valid = yes, Text = There is not enough stock
  ProductNotAvailabe: Responsability = Company, Valid = no, Text = Product is not available
  PaymentNotDone: Responsability = Client, Valid = no, Text = Payment is not done
  BlackListedClient: Responsability = Client, Valid = no, Text = Client is black listed
  NotEnoughTime: Responsability = Company, Valid = yes, Text = There is not enough time

Improvement

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

Имея:

ReasonAttributes.cs

public class ReasonResponsabilityAttribute : Attribute
{
  public Responsability Responsability { get; private set; }
  public ReasonResponsabilityAttribute(Responsability responsability)
  {
    Responsability = responsability;
  }
}

public class ReasonValidAttribute : Attribute
{
  public bool Valid { get; private set; }
  public ReasonValidAttribute(bool valid)
  {
    Valid = valid;
  }
}

public class ReasonTextAttribute : Attribute
{
  public string Text { get; private set; }
  public ReasonTextAttribute(string text)
  {
    Text = text;
  }
}

Reason.cs теперь:

public enum Reason
{

  [ReasonResponsability(Responsability.Company)]
  [ReasonValid(true)]
  [ReasonText("There is not enough stock")]
  NotEnoughStock,

  // ...
  ProductNotAvailabe,

  // ...
  PaymentNotDone,

  // ...
  BlackListedClient,

  // ...
  NotEnoughTime

}

Статический конструктор ReasonMetadata.cs теперь:

using System.Reflection;

static ReasonMetaData()
{
  All = new Dictionary<Reason, ReasonMetaData>();
  var list = Enum.GetValues(typeof(Reason));
  foreach ( Reason reason in list )
  {
    var metadata = new ReasonMetaData();
    var memberinfo = reason.GetType().GetMember(Enum.GetName(typeof(Reason), reason));
    if ( memberinfo.Length == 1 )
    {
      var attributes = memberinfo[0].GetCustomAttributes();
      foreach ( Attribute attribute in attributes )
        if ( attribute is ReasonResponsabilityAttribute )
          metadata.Responsability = ( (ReasonResponsabilityAttribute)attribute ).Responsability;
        else
        if ( attribute is ReasonValidAttribute )
          metadata.Valid = ( (ReasonValidAttribute)attribute ).Valid;
        else
        if ( attribute is ReasonTextAttribute )
          metadata.Text = ( (ReasonTextAttribute)attribute ).Text;
    }
    All.Add(reason, metadata);
  }
}

Использование только одного атрибута

ReasonMetadataAttribute.cs

public class ReasonMetadataAttribute : Attribute
{
  public Responsability Responsability { get; private set; }
  public bool Valid { get; private set; }
  public string Text { get; private set; }
  public ReasonMetadataAttribute(Responsability responsability, bool valid, string text)
  {
    Responsability = responsability;
    Valid = valid;
    Text = text;
  }
}

Статический конструктор ReasonMetadata.cs теперь:

static ReasonMetaData()
{
  All = new Dictionary<Reason, ReasonMetaData>();
  var list = Enum.GetValues(typeof(Reason));
  foreach ( Reason reason in list )
  {
    var metadata = new ReasonMetaData();
    var memberinfo = reason.GetType().GetMember(Enum.GetName(typeof(Reason), reason));
    if ( memberinfo.Length == 1 )
    {
      var attributes = memberinfo[0].GetCustomAttributes();
      foreach ( Attribute attribute in attributes )
        if ( attribute is ReasonMetadataAttribute )
        {
          var metadataAttribute = (ReasonMetadataAttribute)attribute;
          metadata.Responsability = metadataAttribute.Responsability;
          metadata.Valid = metadataAttribute.Valid;
          metadata.Text = metadataAttribute.Text;
        }
    }
    All.Add(reason, metadata);
  }
}

Использование:

public enum Reason
{
  [ReasonMetadata(Responsability.Company, true, "There is not enough stock")]
  NotEnoughStock,
  // ...
  ProductNotAvailabe,
  // ...
  PaymentNotDone,
  // ...
  BlackListedClient,
  // ...
  NotEnoughTime
}
0 голосов
/ 04 ноября 2019

Ваш пример выглядит так:

public enum Reason
{
    None = 0,
    Valid = 1,
    CompanyResponsible = 2,
    ClientResponsible = 4, 

    // Valid = yes, Responsability = company, Text = There is not enough stock
    [Description("There is not enough stock")
    NotEnoughStock = Valid | CompanyResponsible | 8, 
    ...

    // Valid = yes, Responsability = company, Text = There is not enough time
    [Description("There is not enough time")
    NotEnoughTime = Valid | CompanyResponsible | 128,          
}

Возможно, вы можете выделить несколько отдельных концепций для замены | 8 деталей.

0 голосов
/ 04 ноября 2019

Создайте класс для хранения другой информации, например, ReasonDetail, затем используйте Dictionary<Reason, ReasonDetail> и укажите соответствие между Reason и ReasonDetail

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...