55

How can I initialize a const / static array of structs as clearly as possible?

class SomeClass
{

    struct MyStruct
    {
        public string label;
        public int id;
    };

    const MyStruct[] MyArray = {
          {"a", 1}
          {"b", 5}
          {"q", 29}
    };
};
Adam Tegen
  • 25,378
  • 33
  • 125
  • 153

5 Answers5

57

Firstly, do you really have to have a mutable struct? They're almost always a bad idea. Likewise public fields. There are some very occasional contexts in which they're reasonable (usually both parts together, as with ValueTuple) but they're pretty rare in my experience.

Other than that, I'd just create a constructor taking the two bits of data:

class SomeClass
{

    struct MyStruct
    {
        private readonly string label;
        private readonly int id;

        public MyStruct (string label, int id)
        {
            this.label = label;
            this.id = id;
        }

        public string Label { get { return label; } }
        public string Id { get { return id; } }

    }

    static readonly IList<MyStruct> MyArray = new ReadOnlyCollection<MyStruct>
        (new[] {
             new MyStruct ("a", 1),
             new MyStruct ("b", 5),
             new MyStruct ("q", 29)
        });
}

Note the use of ReadOnlyCollection instead of exposing the array itself - this will make it immutable, avoiding the problem exposing arrays directly. (The code show does initialize an array of structs - it then just passes the reference to the constructor of ReadOnlyCollection<>.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    @Chris: No, I'd say it goes far beyond just publicly consumable APIs. How big is your team? Are you *always* going to work for that company? How sure are you that *every member of your team*, now and forever, understands the many and varied oddnesses of mutable structs? For throwaway code, I'd agree... but most code lives much longer in maintenance mode than we originally expect, and poor design can make it much harder to disentangle that later on. You find that someone's passed a field by reference and then you *can't* refactor to properties without breaking it, etc. – Jon Skeet Mar 01 '11 at 06:26
  • Awesome John, you just made me realize a new angle of modern object oriented programming. Thank you.. – Adarsha Mar 02 '12 at 04:43
  • 4
    It is a shame that there is so much extra typing here. In C++11 I can do: `struct foo { int i; const char* str; char c; }; foo f[] = { { 1, "hello", 'c' }, { 2, "goodbye", 'd'" }};` It would be nice if one didn't need to write the constructor or have to type "new foo" so much. – Bklyn May 17 '13 at 14:09
  • 2
    @Jon Skeet: I have a question. Considering that label and id are readonly, is there any reason to allow accessing them only thorugh property instead of let them be public? Does the compiler optimize it, or accessing them through accessor has an overhead respect of accessing them as public fields? – Heisenbug Jul 11 '13 at 08:25
  • Sorry. Why didn't you simplify the code by replacing the read only fields and associated read only properties just with read only automatic properties? – kiss my armpit Jun 13 '14 at 21:53
  • 1
    @FifaEarthCup2014: That would leave the properties being writable within the type - which will be fixed in C# 6, fortunately. At the moment, there's no way of writing a *truly* read-only automatically implemented property. – Jon Skeet Jun 13 '14 at 21:54
  • Supposing I go down this route, and considering this'll mean I'm required to write a constructor, are there any benefits to making `MyStruct` a `struct` rather of a `class`? – antak Oct 28 '14 at 01:22
  • @antak: You sure use a struct if you want it to be a value type - which usually means it should be reasonably small, and treated *as* a value - something like a number or a date/time value. – Jon Skeet Oct 28 '14 at 07:01
  • List and Array are two different things – Tutankhamen Aug 07 '15 at 06:04
  • @Tutankhamen: Indeed - did you read to the end of my answer? "Note the use of ReadOnlyCollection instead of the array itself - this will make it immutable, avoiding the problem exposing arrays directly." My answer *does* create an array, but then wraps it in a read-only collection (which isn't a `List` either, btw). – Jon Skeet Aug 07 '15 at 06:05
  • yes I did and this is why I wrote about your answer here. – Tutankhamen Aug 07 '15 at 06:20
  • @Tutankhamen: I've shown the OP how to create and initialize an array - and also explained why exposing that array directly is a bad thing, and that wrapping it up in a read-only collection is a better idea. The OP is presumably satisfied, given that they accepted the answer... I don't see what you're objecting to. – Jon Skeet Aug 07 '15 at 06:23
  • Exactly! You didn't show how to initialize an ARRAY and this is what I objecting to. – Tutankhamen Aug 07 '15 at 06:38
  • 1
    @Tutankhamen: Again, "I've shown the OP how to create and initialize an array" - what do you think the code: `new[] { new MyStruct ("a", 1), new MyStruct ("b", 5), new MyStruct ("q", 29) }` does? It initializes an array. – Jon Skeet Aug 07 '15 at 07:05
  • @JonSkeet what would be the best way to implement a constructor with a struct that conatins pointers? – DotNet Programmer Oct 28 '15 at 18:58
  • @Kyle: I never use pointers myself. I suggest you ask a new question with appropriate details. – Jon Skeet Oct 28 '15 at 22:44
  • @JonSkeet ok I thought I would just ask since you typically have the best answers and why not ask one of the best on here at giving detailed answers. – DotNet Programmer Oct 30 '15 at 16:42
  • 1
    Stating that mutable structs or public fields are "evil" assumes a lot about the context in which they're used, and using the word "evil" is dogmatic. – Bryan W. Wagner May 03 '19 at 15:07
  • 1
    @BryanW.Wagner: Have reworded this. – Jon Skeet May 03 '19 at 15:23
28

Are you using C# 3.0? You can use object initializers like so:

static MyStruct[] myArray = 
            new MyStruct[]{
                new MyStruct() { id = 1, label = "1" },
                new MyStruct() { id = 2, label = "2" },
                new MyStruct() { id = 3, label = "3" }
            };
Winston Smith
  • 21,585
  • 10
  • 60
  • 75
  • 5
    With C# 3 you don't need the () for each constructor call (as you're using an object initializer) and you can ditch the "new MyStruct[]" bit as that's implied anyway due to being the initializer for an array variable. – Jon Skeet Nov 21 '08 at 17:35
  • To be clear, despite appearances and the lack of "new" this will still allocate an array on the heap, not the stack. – yoyo Apr 29 '15 at 18:51
6

You cannot initialize reference types by default other than null. You have to make them readonly. So this could work;

    readonly MyStruct[] MyArray = new MyStruct[]{
      new MyStruct{ label = "a", id = 1},
      new MyStruct{ label = "b", id = 5},
      new MyStruct{ label = "c", id = 1}
    };
Ali Ersöz
  • 15,860
  • 11
  • 50
  • 64
  • 5
    Note that that only makes the variable readonly - it doesn't prevent other code from replacing elements in the array. – Jon Skeet Nov 21 '08 at 17:36
  • 1
    This solution worked for me. It's a shame that we don't have something as dense and efficient as old C-style array/struct initializers. – David Jeske Jun 17 '13 at 21:56
3

Change const to static readonly and initialise it like this

static readonly MyStruct[] MyArray = new[] {
    new MyStruct { label = "a", id = 1 },
    new MyStruct { label = "b", id = 5 },
    new MyStruct { label = "q", id = 29 }
};
Shreevardhan
  • 12,233
  • 3
  • 36
  • 50
-3

I'd use a static constructor on the class that sets the value of a static readonly array.

public class SomeClass
{
   public readonly MyStruct[] myArray;

   public static SomeClass()
   {
      myArray = { {"foo", "bar"},
                  {"boo", "far"}};
   }
}
Paul Sonier
  • 38,903
  • 3
  • 77
  • 117