在C#中初始化结构体数组

55

我应该如何以最清晰的方式初始化一个const/static结构体数组?

class SomeClass
{

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

    const MyStruct[] MyArray = {
          {"a", 1}
          {"b", 5}
          {"q", 29}
    };
};
5个回答

57

首先,你真的需要一个可变结构体吗?它们几乎总是不好的选择。同样,公共字段也是如此。有一些非常偶然的情况下它们是合理的(通常都是两个部分一起使用,例如ValueTuple),但在我的经验中它们相当少见。

除此之外,我只会创建一个带有这两个数据位的构造函数:

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

注意使用ReadOnlyCollection代替直接暴露数组 - 这将使其成为不可变的,避免直接暴露数组可能导致的问题。(代码示例初始化了一个结构体数组 - 然后只是将引用传递给ReadOnlyCollection<>的构造函数。)


2
@Chris:不,我认为它远远超出了仅仅公开可用的API。你的团队有多大?你会一直为那家公司工作吗?你有多确定你的团队中的每个成员,现在和将来,都理解可变结构的许多奇怪之处?对于一次性代码,我同意......但是大多数代码的维护时间比我们最初预期的要长得多,而糟糕的设计可能会使以后的解脱变得更加困难。你会发现有人通过引用传递了一个字段,然后你就无法重构为属性而不破坏它,等等。 - Jon Skeet
4
这里有太多冗余的输入,真是件遗憾。在C++11中,我可以这样做: struct foo { int i; const char* str; char c; }; foo f[] = { { 1, "hello", 'c' }, { 2, "goodbye", 'd'" }};如果不需要编写构造函数或者经常输入"new foo"就更好了。 - Bklyn
2
@Jon Skeet:我有一个问题。考虑到标签和ID是只读的,是否有理由仅通过属性访问它们而不是让它们成为公共的?编译器是否会进行优化,或者通过访问器访问它们相对于作为公共字段访问它们具有开销? - Heisenbug
1
@FifaEarthCup2014:这将使属性在类型内可写 - 幸运的是,这将在C# 6中得到修复。目前,没有办法编写真正只读的自动实现属性。 - Jon Skeet
1
@Tutankhamen:再次强调,“我已经向OP展示了如何创建和初始化数组”- 你认为这段代码:new[] { new MyStruct ("a", 1), new MyStruct ("b", 5), new MyStruct ("q", 29) } 是做什么的?它是用来初始化一个数组的。 - Jon Skeet
显示剩余14条评论

28

你是否正在使用C# 3.0?你可以使用对象初始化器,像这样:

static MyStruct[] myArray = 
            new MyStruct[]{
                new MyStruct() { id = 1, label = "1" },
                new MyStruct() { id = 2, label = "2" },
                new MyStruct() { id = 3, label = "3" }
            };

5
在使用对象初始化器时,使用C# 3不需要为每个构造函数调用添加(),并且可以省略"new MyStruct[]"部分,因为它已经被暗示为数组变量的初始化器。 - Jon Skeet
要澄清一点,尽管看起来没有“新”的地方,但这仍会在堆上分配一个数组,而不是栈。 - yoyo

6

除了null之外,您不能默认初始化引用类型。您必须使它们只读。因此,可以这样做;

    readonly MyStruct[] MyArray = new MyStruct[]{
      new MyStruct{ label = "a", id = 1},
      new MyStruct{ label = "b", id = 5},
      new MyStruct{ label = "c", id = 1}
    };

5
请注意,这只会使变量为只读 - 它不能防止其他代码替换数组中的元素。 - Jon Skeet
1
这个解决方案对我很有效。遗憾的是,我们没有像旧的C风格数组/结构体初始化器那样密集和高效的东西。 - David Jeske

3

const更改为static readonly,并像这样初始化它

static readonly MyStruct[] MyArray = new[] {
    new MyStruct { label = "a", id = 1 },
    new MyStruct { label = "b", id = 5 },
    new MyStruct { label = "q", id = 29 }
};

-3
我会在类上使用静态构造函数来设置静态只读数组的值。
public class SomeClass
{
   public readonly MyStruct[] myArray;

   public static SomeClass()
   {
      myArray = { {"foo", "bar"},
                  {"boo", "far"}};
   }
}

静态构造函数不允许使用访问修饰符。 - Philipp M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接