10

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.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Darcy
  • 129
  • 7
  • 4
    If you really come from a C background, you'll see soon that C# is no good... +1 anyways for a good question. –  Dec 11 '12 at 20:34
  • 6
    @H2CO3: discutable comment. Every language has it's appropriate use and place in the world, especilly the languages widely use like `C#`. If you get `F1` super car to drive over golf fields, it will lead to failure. Let's use beautiful tools for appropriate places we have... – Tigran Dec 11 '12 at 20:39
  • @Tigran That metaphore is worth an upvote. –  Dec 11 '12 at 20:40
  • 1
    @Tigran I agree... in a lot of ways C allows a good programmer to do some very powerful stuff. It also allows bad programmers to do some truely horrible things though! :) – Darcy Dec 11 '12 at 20:43
  • What I mean, is that with `C#` for 90% cases you can do **much more** stuff then you do in `C` or `C++`, even if you feel your self highly productive in that languages. `.NET` is *extremely* powerful and productive environment, do not understimate it. Sure, I repeat, if you're writing compiler, driver, game, CAD programs, it's better to choose something else, if you can. – Tigran Dec 11 '12 at 20:46
  • @Tigran You might mean to say that you can do much more with less effort than in C or C++. It's hard to argue that C# is actually more powerful than C or C++, but the .NET framework does allow for people to do more with less work on their part. It's a tool for amplifying work. – Rob Ocel Dec 11 '12 at 20:53
  • @Takkara: it is *much more powerful* like an environment (.net framework in general), I don't mean a *language* itself, .NET world. – Tigran Dec 11 '12 at 20:56

3 Answers3

4

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)
            });
Joe
  • 122,218
  • 32
  • 205
  • 338
  • +1 for the practical c# approach. You can even use anonymous types for more flexibility. – oɔɯǝɹ Dec 11 '12 at 20:52
  • Crikey... Is that really an equivilent sort-of method to dealing with table data? Thanks, I'll start having a read-up. A good example of where C let's you handle raw data with less effort :) – Darcy Dec 11 '12 at 20:53
  • @Joe Maybe my mention of it being const isn't actually necessary... I'm accustomed to doing that out of good practice (stores data in flash instead of RAM), but maybe that's not even necessary in C#? In which case, would I just use Collection? – Darcy Dec 11 '12 at 21:00
  • 1
    why create an array just to call `ToList` on it? Just use `new List` instead of `new []`. – Servy Dec 11 '12 at 21:13
  • 1
    C# does not provide guarantees of constness the way C does, primarily because C's 'guarantee' isn't one. Attempting to enforce that idiom upon C# is inevitably going to lead to additional code. If you _really need_ the table to be unmodifiable, this is the correct approach. Otherwise, you'd probably be just as well-off using a readonly array in conjunction with an appropriate level of encapsulation. Note also that this answer adds some code by expanding your data structure into a class, which is more idiomatic in C#; it is possible to match your original code more closely, if you want. – Cole Campbell Dec 11 '12 at 21:13
  • @ColeCampbell Hi, do you have a simple example please? – Darcy Dec 11 '12 at 21:23
  • @Servy - "why create an array just to call ToList on it" - laziness :) – Joe Dec 11 '12 at 22:00
  • @Darcy "Maybe my mention of it being const isn't actually necessary" - if you don't need the collection to be readonly ("const"), then you can use an array or a List. – Joe Dec 11 '12 at 22:03
  • @Darcy: I have posted an answer which elaborates on my thoughts. – Cole Campbell Dec 12 '12 at 15:39
2

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.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • I didn't think C# supported typedef? – Darcy Dec 11 '12 at 20:33
  • @DarcyWilliams: no there is no no `typedef` and no `#define` (in sence of creating powerful macros). You can use `#define` like conditional compilation dirrective and that's it. – Tigran Dec 11 '12 at 20:41
  • Hi, I don't think that's really answering the question sorry... I'm interested in how tables of data can be used to drive C# methods, not whether pointers are constant (for example). – Darcy Dec 11 '12 at 20:41
  • @DarcyWilliams: What do you mean ? You ask about possible datastructres and I provided them in my answer. Am I missing something ? – Tigran Dec 11 '12 at 20:43
  • 1
    @DarcyWilliams - C# doesn't support `typedef`. You'd need a new structure. – prprcupofcoffee Dec 11 '12 at 20:45
  • @Tigran If I can't use typedef how does your example help given that it's using a typedef'd structure? (my_data_t) I'm genuinely confused... – Darcy Dec 11 '12 at 20:46
  • @David Yep, thanks, that was kind of what I was getting at :) – Darcy Dec 11 '12 at 20:47
  • @DarcyWilliams: you need to define a `new` type that contains that type of fields, new class (say) – Tigran Dec 11 '12 at 20:48
  • @DarcyWilliams - So after you define your new `public struct my_data_t { stuff }`, then you can use the `public static readonly my_data_t[] ref_data` syntax. – prprcupofcoffee Dec 11 '12 at 20:48
  • @Tigran Yep, that's what I'm trying to determine... suggestions please? – Darcy Dec 11 '12 at 20:49
  • @DarcyWilliams: I edited my answer with more concrete (I hope) example. Hope this helps. – Tigran Dec 11 '12 at 20:51
1

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.

Community
  • 1
  • 1
Cole Campbell
  • 4,846
  • 1
  • 17
  • 20
  • Some nitpicking: "ReadOnlyCollection ... can wrap any object which implements the IEnumerable interface" - no, it wraps an IList, there is no constructor that takes an IEnumerable. Also your "ReferenceTable" property should probably not instantiate a new ReadOnlyCollection each time it's called. – Joe Dec 12 '12 at 16:30
  • You're right; my memory failed me. I updated my answer. As for your second point, I agree with you, but it's an obvious enough optimization that I didn't want to clutter up my example with it. – Cole Campbell Dec 12 '12 at 16:49
  • On further consideration, I changed my answer again to reflect your concerns. – Cole Campbell Dec 12 '12 at 16:59
  • @ColeCampbell Thanks mate, that's very similar to what I ended up implementing... minus the ReadOnly aspect. That I realised was more of a left over I'm familiar with using in C to ensure data is loaded from flash rather than copied to RAM. Thanks – Darcy Dec 20 '12 at 02:06