我应该使用枚举、静态类、字典还是结构体来表示这些“标记为浮点数”的东西?在c#

本文关键字:浮点数 记为 静态类 枚举 字典 表示 结构体 我应该 | 更新日期: 2023-09-27 18:07:20

我有一个恒定的数据结构,它表示每个人脊椎骨的相对高度,与脊柱总高度相关。这是由人体测量学研究等得出的。

我已经在Python中实现了它作为元组的元组,每个元组包含一个(string)Name和(double)Value,如下所示:

vertebral_heights = (
("C7",  0.0000000),
("T1",  0.0391914),
("T2",  0.0785479),
("T3",  0.1183993),
("T4",  0.1590759),
("T5",  0.2009076),
("T6",  0.2442244),
("T7",  0.2893564),
("T8",  0.3366337),
("T9",  0.3863861),
("T10", 0.4389439),
("T11", 0.4946370),
("T12", 0.5537954),
("L1",  0.6167492),
("L2",  0.6838284),
("L3",  0.7553630),
("L4",  0.8316832),
("L5",  0.9131188),
("S1",  1.0000000))

我的第一个想法是创建一个Dictionary,但这需要一个类作为容器使用。然后枚举的想法出现在脑海中,但我读过"枚举用于整型",并且我有双精度。然后是类和结构,但到目前为止,我完全困惑了,我相信我目前对c#中做这些事情的最佳实践的理解还不够。

我的预期用途是在应用程序模型(元素的数字部分)和用户模型(元素的命名的、与域相关的部分)之间有一个"映射"。

任何建议吗?

我应该使用枚举、静态类、字典还是结构体来表示这些“标记为浮点数”的东西?在c#

这取决于你想如何访问这些值。

<<p> 常量/strong>

如果你总是使用变量名,例如:

double x = C7;

,那么你可以使用一个充满常量的类,像这样:

public class VertebralHeights
{
    public const double C7 = 0.0000000d;
}
字典

但是,如果您想动态访问它们,例如:

string id = "C7";
double x = VertebralHeights[id];

那么你最好使用Dictionary,你可以这样定义:

Dictionary<string, double> VertebralHeights = new Dictionary<string, double>()
{
    { "C7", 0.0000000d },
    { "T1", 0.0391914d}
}

两路合一

如果你想同时对值进行强类型访问和动态访问,你可以扩展上面的任何一个方法…

对于常量(方法1)添加一个接受字符串的函数:

public double GetValue(string s)
{
    switch(s)
    {
        case "C7": return C7;
        case "T7": return T7;
        //...and so on...
        default: return 0;//or an alternate default
    }
}

(注意:你可以用反射来代替,这对于一个大列表来说会更容易,但是不值得在这里付出额外的性能损失)

对于Dictionary方法(方法2),您可以添加一个getter集合:

public double C7 { get { return VertebralHeights["C7"]; } }

这是我的看法-使用一个字典的单例类:

public class Vertebrae : Dictionary<string, double>
{
    private Vertebrae() : base() { }

    private static Vertebrae _heights = new Vertebrae() {
        { "C7", 0.0 },
        { "T1", 0.0391914 },
        { "T2", 0.0785479 },
    };
    public static Vertebrae Heights { get { return _heights; } }
    public static double C7 { get { return Heights["C7"]; } }
    public static double T1 { get { return Heights["T1"]; } }
    public static double T2 { get { return Heights["T2"]; } }
    public static IEnumerable<double> All
    {
        get
        {
            return new List<double>() { C7, T1, T2 };
        }
    }
}

通过字符串名称访问椎骨,您可以这样做:

double c7 = Vertebrae.Heights["C7"];

要通过符号名访问椎骨,可以这样做:

double c7 = Vertebrae.C7;

枚举椎骨:

foreach (double v in Vertebrae.All) { /* ... */ }

对于枚举器,您可以像在枚举器中初始化单个静态列表,但我不确定静态列表或静态字典哪个会先初始化…

将其作为枚举,并预先编写黑盒管道代码。你不会后悔的!下面是我要做的:

编写一个自定义属性,以便您可以将双精度值关联到每个enum:

[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
internal sealed class VertebralHeightAsDoubleAttribute : Attribute
{
  public double HeightValue { get; private set; }
  public VertebralHeightAsDoubleAttribute(double heightValue_)
  {
    HeightValue = heightValue_;
  }
}   

一些使生活更容易的扩展方法:

public static class VHAttribExtensions
{
  public static string ToNameString(this VertebralHeight target)
  {
    return Enum.GetName(typeof(VertebralHeight), target);
  }
  public static double ToHeightValue(this VertebralHeight target)
  {
    var fi = target.GetType().GetField(target.ToString());
    var attributes = (VertebralHeightAsDoubleAttribute[])fi.GetCustomAttributes(
      typeof(VertebralHeightAsDoubleAttribute), false);
    return attributes.Length > 0 ? attributes[0].HeightValue : double.NaN;
  }
}

使用自定义属性定义枚举:

public enum VertebralHeight
{
  [VertebralHeightAsDouble(0.0000000)]
  C7,
  [VertebralHeightAsDouble(0.0391914)]
  T1,
  [VertebralHeightAsDouble(0.0785479)]
  T2,
  [VertebralHeightAsDouble(0.1183993)]
  T3,
  [VertebralHeightAsDouble(0.1590759)]
  T4,
  [VertebralHeightAsDouble(0.2009076)]
  T5,
  [VertebralHeightAsDouble(0.2442244)]
  T6,
  [VertebralHeightAsDouble(0.2893564)]
  T7,
  [VertebralHeightAsDouble(0.3366337)]
  T8,
  [VertebralHeightAsDouble(0.3863861)]
  T9,
  [VertebralHeightAsDouble(0.4389439)]
  T10,
  [VertebralHeightAsDouble(0.4946370)]
  T11,
  [VertebralHeightAsDouble(0.5537954)]
  T12,
  [VertebralHeightAsDouble(0.6167492)]
  L1,
  [VertebralHeightAsDouble(0.6838284)]
  L2,
  [VertebralHeightAsDouble(0.7553630)]
  L3,
  [VertebralHeightAsDouble(0.8316832)]
  L4,
  [VertebralHeightAsDouble(0.9131188)]
  L5,
  [VertebralHeightAsDouble(1.0000000)]
  S1
}
测试:

static void Main(string[] args)
{
  var list = Enum.GetValues(typeof(VertebralHeight)).OfType<VertebralHeight>();
  foreach (var vh in list)
  {
    Console.WriteLine("{0} : {1}", vh.ToNameString(), vh.ToHeightValue());
  }
  Console.ReadLine();
}

您可以创建一个类:

public static class VertebralHeights
{
    public const double C7 = 0.0000000;
    public const double T1 = 0.0391914;
    //...
}

Access: double c7 = VertebralHeights.C7;

取决于您如何使用这些映射。如果需要按名称(string)进行查找,那么Dictionary是正确的选择。但是,如果您只需要这些数字具有友好的名称,我会选择类中的常量(可能是静态的)。

在字典中枚举键和值也很容易:

var dict = new Dictionary<string, double>();
foreach (var key in dict.Keys)
{
}
foreach (var value in dict.Values)
{
}

要枚举它们,按字符串查找它们,并按顺序对它们排序,您需要存储三段数据。访问它们的方式会改变存储它们的最佳方式。

  1. List包含名称和值的元组。
    • 这具有排序的主要优点。
    • 为了通过名称获得特定的项目,您需要使用linq查询来检索它。
  2. Dictionary,键为name,值为order和value的元组
    • 这样做的主要优点是可以通过名称快速查找物品
    • 为了让所有的项目在一个特定的顺序,你需要一个linq查询来订购它们。
    • 如果你只是想在没有特定订单的情况下循环所有物品,Dictionary将允许。
  3. 保留一个字典和一个值列表。-有点乱,可能是过度优化。
  4. 创建一个自定义集合,该集合继承了上述实现之一,并提供了隐藏实际实现的缺失功能。Linq还没有慢到令人担忧的短列表。

可以在泛型集合中使用一个简单的类。然后,LINQ可以很容易地将值相互映射。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
    class Vertebra {
        public string name { get; set; }
        public double height { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            List<Vertebra> Vertebrae = new List<Vertebra>() {
                new Vertebra() {name = "C7", height = 0.0000000},
                new Vertebra() {name = "T1", height = 0.0391914}
                //etc
            };
            //find height by name:
            double H = Vertebrae.Single(v => v.name == "C7").height;
            //find name by height:
            string N = Vertebrae.Single(v => v.height == 0.0391914).name;
        }
    }
}