简体   繁体   中英

Coming from a C background, what's a good way to implement const reference data tables/structures in C#?

I'll give a quick example of what I'm familiar with implementing using C . The focus I think is on how the data can be used, not so much what I'm doing with it in the example :)

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);
}

So I'm looking for advice on how similar data tables, or reference data, are implemented in C#. I'm getting the hang of the higher level side of things, but I haven't dealt with any table driven data methods yet.

As an example, what I might be trying to do in C# is to have a combo box allowing selection from the description field, while the colour id and quantity might be used to update read-only boxes.

That's a really simple example, but if I can determine a good way to implement that, I can extrapolate that to what I'm actually doing.

I'd use a ReadOnlyCollection<T> of an immutable class.

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)
            });

This

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

can be substituted with readonly modifier in C# , like

//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},
};

where ref_data (in this case) is something like

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
}

The same is valid for constant const char *description , use readonly .

I repeat, this is a by convention, as theoretically there is a, by the way, ways to change a data or trick access to it.

There is no concept of constant pointer in C# , as pointers (in managed memory) constantly moved all arround, as Garbage Collector continuously shrinks (defrags) memory, in order to avoid memory fragmentation, which brings us a benefit of fast allocations.

There is another option too (don't know if this is a suitable in your case or not), you can use unmanged access to the code, via unsafe modifier and keep all your C/C++ pointer stuff inside. In this way, you say to Grabage Collector: "wait, I know what I'm doing", so the all memory management has to be handled by you (inside that unmanaged code) as if you write ordinary C/C++ code.

I'm posting this answer in response to your request that I elaborate on my comment in Joe's answer.

First point: if you need my_data_t to be a struct for whatever reason, C# does support those. You don't have to upgrade it to a class, as Joe did, unless you want to.

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

This is a mutable struct. Given an instance of this struct, I can modify its values if I so choose. Most people would say that mutable structs are evil. As a game developer, I would say that mutable structs are enormously important , but also dangerous .

The mutable fields and properties of any object can be initialized using the object initializer syntax, which is probably most analogous to what you're doing in C:

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

Personally I think this is a bit unwieldy, especially for large structs, but it is available if you want to use it.

You can change the struct to be immutable by adding readonly modifiers to its fields:

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;
} 

Note that readonly only prevents the object references from being changed. It does not prevent the objects themselves from being mutated, if they're mutable.

Note also that I've also added a constructor. This is because readonly fields can only be set in a static initializer or inside of the object's constructor (barring some trickery). Here, you would initialize a new instance of MyData as in Joe's answer:

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

Second point: constness, or the lack thereof. C# doesn't support C-style constness because C-style constness is broken . Eric Lippert, formerly a developer on the C# language team at Microsoft, elaborated on that point here .

It is far better, I think, not to worry about emulating C-style constness unless you really, really, really have a good reason to. Constness is ultimately a way to prevent your code from being tampered with by the malicious or the ignorant. The malicious are going to be able to mutate your data whether you like it or not--in C, and in C#--and we have a much better tool for protecting yourself from the ignorance of others: encapsulation!

Wrap the functionality that uses this table inside of a class, make the table a private member of that class, and then don't mutate it . If at some point you do need to expose that table to the outside world, then you can use ReadOnlyCollection as Joe suggested:

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

ReadOnlyCollection is just a thin wrapper around some other collection, in this case, your data table. It can wrap any object which implements the IList<T> interface, which includes arrays and several of the built-in collections.

One more note: in one of your comments you mentioned that one of the reasons for declaring the table const in C is because it influences where the object is allocated in memory. This is not the case here. In the example above, RefData will be declared on the managed heap, because it's an array, and arrays are reference types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM