[英]Coming from a C background, what's a good way to implement const reference data tables/structures in C#?
我将给出一个简短的示例,说明我对使用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和数量可能会用于更新只读框。
这是一个非常简单的示例,但是如果我可以确定实现此目标的好方法,则可以将其推断为我的实际工作。
我会使用不可变类的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)
});
这个
const my_data_t ref_data[] =
{
{"Brown Bear", 0x88, 10},
{"Blue Horse", 0x666, 42},
{"Purple Cat", 123456, 50},
};
可以用C#
readonly
修饰符替换,例如
//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++
代码一样。
我正在发布此答案,以回应您对我在Joe答案中的评论进行详细说明的请求。
第一点:如果出于某种原因需要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
字段只能在静态初始化程序中或在对象的构造函数内部设置(除非有一些技巧)。 在这里,您将按照Joe的回答初始化MyData
的新实例:
MyData x = new MyData("Brown Bear", 0x88, 10);
第二点:保持不变或缺乏稳定。 C#不支持C样式常量,因为C样式常量已损坏 。 埃里克利珀,以前在C#语言的团队在微软开发人员,详细阐述了这一点这里 。
我认为,除非您真的,真的,真的有充分的理由这么做,否则不要担心仿效C样式的常数性。 常量最终是防止您的代码被恶意或无知者篡改的一种方法。 无论您是否喜欢,无论是在C中还是在C#中,恶意软件都将能够变异您的数据,并且我们有一个更好的工具来保护自己免受他人的无知:封装!
将使用该表的功能包装到类中,使该表成为该类的私有成员,然后不要对其进行突变 。 如果确实需要将该表暴露给外界, 则可以按照Joe的建议使用ReadOnlyCollection
:
public static readonly ReadOnlyCollection<MyData> ReferenceTable = new ReadOnlyCollection<MyData>(new []
{
new MyData(/* whatever */),
new MyData(/* whatever */),
new MyData(/* whatever */),
});
ReadOnlyCollection
只是其他一些集合(在本例中为数据表)的精简包装。 它可以包装实现IList<T>
接口的任何对象,该接口包括数组和一些内置集合。
还有一点需要注意:在您的评论之一中,您提到了在C中声明表const
的原因之一是因为它影响对象在内存中的分配位置。 这里不是这种情况。 在上面的示例中, RefData
将在托管堆上声明,因为它是一个数组,并且数组是引用类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.