10

我将给出一个我熟悉的使用C实现的简单示例。我认为的重点是如何使用数据,而不是我在示例中用它做什么:)

typedef struct  
{
  const char  *description;
  uint32_t    colour_id;      
  uint32_t    quantity;
} my_data_t;

const my_data_t ref_data[] =
{
  {"Brown Bear", 0x88,   10},
  {"Blue Horse", 0x666,  42},
  {"Purple Cat", 123456, 50},
};

void show_animals( void )
{
  my_data_t *ptr;

  ptr = &ref_data[2];

  console_write("Animal: %s, Colour: 0x%8X, Num: %d", 
      ptr->description,
      ptr->colour_id,
      ptr->quantity);
}

因此,我正在寻找有关如何在 C# 中实现类似数据表或参考数据的建议。我掌握了更高层次的东西,但我还没有处理任何表驱动的数据方法。

例如,我可能在 C# 中尝试做的是有一个组合框,允许从描述字段中进行选择,而颜色 id数量可能用于更新只读框。

这是一个非常简单的例子,但如果我能确定一种实现它的好方法,我可以将其推断为我实际正在做的事情。

4

3 回答 3

4

我会使用一个ReadOnlyCollection<T>不可变的类。

public class MyData
{
    public MyData(string description, int colorId, int quantity)
    {
        Description = description;
        ColorId = colorId;
        Quantity = quantity;
    }
    public string Description {get; private set; }
    public int ColorId {get; private set; }
    public int Quantity {get; private set; }
}


...

public static readonly ReadOnlyCollection<MyData> refData =
    new ReadOnlyCollection<MyData>(
        new [] {
            new MyData("Brown Bear", 0x88,   10),
            new MyData("Blue Horse", 0x666,  42),
            new MyData("Purple Cat", 123456, 50)
            });
于 2012-12-11T20:49:19.460 回答
2

const my_data_t ref_data[] =
{
  {"Brown Bear", 0x88,   10},
  {"Blue Horse", 0x666,  42},
  {"Purple Cat", 123456, 50},
};

可以用readonly修饰符 in代替C#,例如

//INITIALIZED ONES AND NEVER CHANGED, BY CONVENTION
public static readonly ref_data[] my_data_t = new ref_data[] =
{
  new ref_data{Animal = "Brown Bear", Code = 0x88,   Index = 10},
  new ref_data{Animal = "Blue Horse", Code = 0x666,  Index = 42},
  new ref_data{Animal = "Purple Cat", Code = 123456, index = 50},
};

其中ref_data(在这种情况下)类似于

public class ref_data
{
   public string Animal {get;set;}
   public int    Code   {get;set;}  //GUESS, PUT APPROPRIATE NAME
   public int    Index  {get;set;}  //GUESS, PUT APPROPRIATE NAME
}

同样适用于常量const char *description,使用readonly.

我再说一遍,这是一种惯例,因为理论上有一种方法可以更改数据或欺骗对它的访问。

中没有常量指针的概念C#,因为指针(在托管内存中)不断移动,垃圾收集器不断收缩(碎片整理)内存,以避免内存碎片,这给我们带来了快速分配的好处。

还有另一种选择(不知道这是否适合您的情况),您可以通过修饰符使用对代码的无管理访问unsafe并将所有C/C++指针内容保留在里面。这样,您对 Grabage Collector 说:“等等,我知道我在做什么”,因此所有内存管理都必须由您(在该非托管代码中)处理,就像您编写普通C/C++代码一样。

于 2012-12-11T20:31:29.913 回答
1

我发布此答案是为了响应您在乔的回答中详细说明我的评论的请求。

第一点:如果您my_data_t出于某种原因需要成为结构,C# 确实支持这些。除非您愿意,否则您不必像 Joe 那样将其升级到类。

public struct MyData
{
    public string Description;
    public uint ColourID;
    public uint Quantity;
} 

这是一个可变结构。给定这个结构的一个实例,如果我愿意,我可以修改它的值。大多数人会说可变结构是邪恶的。作为游戏开发者,我想说可变结构非常重要,但也很危险

任何对象的可变字段和属性都可以使用对象初始化器语法进行初始化,这可能最类似于您在 C 中所做的事情:

MyData x = { Description = "Brown Bear", ColourID = 0x88, Quantity = 10 };

我个人认为这有点笨拙,特别是对于大型结构,但如果您想使用它,它是可用的。

readonly您可以通过向其字段添加修饰符来将结构更改为不可变:

public struct MyData
{
    public MyData(string description, uint colourID, uint quantity)
    {
        this.Description = description;
        this.ColourID = colourID;
        this.Quantity = quantity;
    }
    public readonly string Description;
    public readonly uint ColourID;
    public readonly uint Quantity;
} 

请注意,readonly这只防止对象引用被更改。如果对象是可变的,它不会阻止对象本身发生变异。

另请注意,我还添加了一个构造函数。这是因为readonly字段只能在静态初始化程序或对象的构造函数内部设置(除非有一些技巧)。MyData在这里,您将在 Joe 的回答中初始化 as 的新实例:

MyData x = new MyData("Brown Bear", 0x88, 10);

第二点:恒常性,或缺乏恒常性。C# 不支持 C-style constness,因为 C-style constness is brokenEric Lippert 曾是 Microsoft C# 语言团队的一名开发人员,他在此处详细阐述了这一点。

我认为,除非你真的、真的、真的有充分的理由,否则不用担心模拟 C 风格的 constness 会好得多。一致性最终是一种防止您的代码被恶意或无知者篡改的方法。不管你喜欢与否,恶意软件都能够改变你的数据——在 C 和 C# 中——我们有一个更好的工具来保护自己免受他人的无知:封装!

将使用该表的功能包装在一个类中,使该表成为该类的私有成员,然后不要改变它。如果在某些时候您确实需要将该表暴露给外界,那么您可以ReadOnlyCollection按照 Joe 的建议使用:

public static readonly ReadOnlyCollection<MyData> ReferenceTable = new ReadOnlyCollection<MyData>(new []
{
    new MyData(/* whatever */),
    new MyData(/* whatever */),
    new MyData(/* whatever */),
});

ReadOnlyCollection只是其他一些集合的薄包装,在这种情况下,您的数据表。它可以包装任何实现IList<T>接口的对象,包括数组和一些内置集合。

还有一点需要注意:在您的一条评论中,您提到const在 C 中声明表的原因之一是因为它会影响对象在内存中的分配位置。这不是这里的情况。在上面的例子中,RefData会在托管堆上声明,因为它是一个数组,而数组是引用类型。

于 2012-12-12T15:37:08.237 回答