tags:

views:

349

answers:

6

It seems strange that the language apparently includes no suitable functionality.

I find myself with data that would best be expressed as a multi-dimensional array but it's utterly constant, there is no way anyone could want to change it without also changing the associated code. Faced with such stuff in Delphi the answer is obvious--a constant whose value is the table. However, C# doesn't seem to support anything like this.

Google shows many people griping about this, no good answers.

How do people handle this sort of situation?

(And don't say that constants don't belong in code--the last one I bumped into was all possible permutations of 4 items. Unless the very nature of spacetime changes this is set in stone.)

What happened?? There was an answer that came pretty close, I was asking about a detail and it vanished! Simply declaring an array sort of does the job--the only problem is that the array allocation is going to run every time. The one in front of me contains 96 values--how do I get it to initialize only once? Do I just have to accept scoping it far wider than it should be? (As it stands it's in one 3-line routine that's inside what amounts to an O(n^3) routine.)

A: 

Enumerations, surely.

Peter Wone
He wants to look up values in a multidimensional table. There is no way you could use enumerations for this.
Mike Daniels
aha yes, suddenly the question makes more sense
Peter Wone
But I still don't see what the fuss is about. There's no need to make the table *incapable* of accepting updates. All he has to do is refrain from writing code that updates it.
Peter Wone
Peter, by that measure, keywords like "const" and "readonly" should be thrown out, right? :)
Steven Sudit
I wouldn't throw them out. They are harmless, the compiler is stable and it would for some code represent a breaking change. But you are right that I wouldn't have added them in the first place. I've always found a language more interesting for what *can* be expressed than what can't.
Peter Wone
+2  A: 

ReadOnlyCollection

recursive
A: 

Well, I've taken the approach of the following, it's a little nasty to read but easy to edit.

 public struct Something
 {
  public readonly int Number;
  public readonly string Name;
  public Something(int num, string name) { this.Number = num; this.Name = name; }
 }

 public readonly Something[] GlobalCollection = new Something[] 
 {
  new Something(1, "One"),
  new Something(2, "Two"),
 };
csharptest.net
This isn't entirely constant. The "readonly" prevents you from swapping out GlobalCollection with another Something[], but it doesn't stop you from swapping out GlobalCollection[0] or [1].
Steven Sudit
A: 

There's a page in in the C# FAQ about this specific thing.

They suggest using a static readonly array:

static readonly int[,] constIntArray = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }};

However, be aware that this is only sort of constant - you can still reassign individual elements within the array. Also, this has to be specified on the class level since it's a static, but it will work fairly well.

Reed Copsey
How do you get the compiler to take this?
Loren Pechtel
Loren: I edited my answer to be multidimensional + more explicit. It works exactly as typed, but you have to specify it at the class/struct level, not in a method.
Reed Copsey
A: 

You could use a readonly Hashtable. The only downside is that readonly does not prevent you from changing the value of a particular item in the Hashtable. So it is not truly const.

readonly Hashtable table = new Hashtable(){{1,"One"},{2,"Two"}};

Or an array

public readonly string[,] arry = new string[,]{{"1","2"},{"2","4"}};

Yes, you will need to declare the variable in the appropriate scope so it does not get initialized more than once.

Douglas
+1  A: 

Like they say, just add another layer of indirection. C# doesn't need to provide a specialized data structure as a language primitive, although one does, at times, wish there was a way to make any class immutable, but that's another discussion.

Now you didn't mention if you need to store different things in there. In fact you didn't mention anything other than multi-dimensional and no ability to change the values or the arrays. I don't even know if the access pattern (a single int,int,int indexer) is appropriate.

But in general, for a 3-dimensional jagged array, the following works (but it isn't pretty).

One caveat is the type you construct it with also needs to be immutable, but that's your problem. You can just create your own read-only wrapper.

public static readonly ReadOnlyThreeDimensions<int> MyGlobalThree 
    = new ReadOnlyThreeDimensions<int>(IntInitializer);

public class ReadOnlyThreeDimensions<T> 
{
    private T[][][] _arrayOfT;

    public ReadOnlyThreeDimensions(Func<T[][][]> initializer)
    {
        _arrayOfT = initializer();
    }

    public ReadOnlyThreeDimensions(T[][][] arrayOfT)
    {
        _arrayOfT = arrayOfT;
    }

    public T this [int x, int y, int z]
    {
        get 
        {
            return _arrayOfT[x][y][z];
        } 
    }
}

And then you just need to provide some initializer method, or assign it in a static constructor.

public static int[][][] IntInitializer()
{
    return xyz // something that constructs a [][][]  
}
Robert Paulson
I think this is the right idea, though the implementation could be improved upon. For one thing, if you copy the arrays that are passed in, the original arrays can be changed. Then again, if the arrays are literals passed in during construction, there's no way to reference them from outside, so no reason to copy.
Steven Sudit
you could always change that constructor to make a new T[][][] and copy all the values, but as the use-case appeared to be it's set once, it didn't seem necessary.
Robert Paulson