声明一个长的常量字节数组

23

我有一个很长的字节数组需要在我的C#代码中声明。我会像这样做:

public static class Definitions
{
    public const byte[] gLongByteArray = new byte[] { 
        1, 2, 3,
        //and so on
    };
}

但我收到一个错误,说常量数组只能用 null 初始化。

如果我把 const 改成 static,代码就会编译通过。但我有一个问题:如果我将它声明为 public static byte[] gLongByteArray,那么它不会在每次加载我的应用程序时被初始化,对吗?在这种情况下,gLongByteArray 只是指向在编译的 exe/dll 文件中定义的数组,它加载到内存中。我问这个问题的原因是因为这个数组相当长,我不想让我的程序在每次启动应用程序时浪费 CPU 周期来加载它,更糟糕的是,这个类被引用了……


2
正如其他人所说,不要根据猜测做出性能决策;要根据事实做出决策。创建一个真实大小的数组,并测量启动性能,并在有或没有它的情况下进行测试。然后你就会知道它是否有影响。使用常量初始化的大字节数组会被合理高效地处理。 - Eric Lippert
2
@ahmd0:请再读一遍我的评论。如果调试器已连接,则生成的代码通常比未连接时效率低。如果您想在调试器中查看实际生成的代码,则必须在您要检查的代码已经被JIT编译之后附加到程序的发布版本。如果您不这样做,那么再次说,您可能看不到客户正在运行的相同代码 - Eric Lippert
3
但更普遍地说:我不能通过查看生成的代码告诉您它运行的速度。如果这是您拥有的超能力,那太棒了,但大多数人没有这种特殊能力。所以,我不会花时间培养这种超能力,而是使用性能分析工具。 - Eric Lippert
5
经常有这样的经历,人们往往从他们自己的角度去看待别人。你用一种讽刺性的修辞疑问句说“听过调试器吗?”然后假设我的回答同样是讽刺的。实际上不是这样的,我正在免费给你提供优秀的建议。很遗憾听到您并不欣赏。 - Eric Lippert
2
有时候我只是希望 Stack Overflow 允许对评论进行踩。 - Dyppl
显示剩余6条评论
4个回答

46

编译时常量(即使用const关键字声明的常量)受到严格限制。获取此类常量不得执行任何代码,否则它将无法成为编译时常量。默认情况下,const常量是static的。

如果您想创建一个常量,但不能使用编译时常量,则可以使用static readonly代替:

public static readonly byte[] longByteArray = new byte[] { 1, 2, 3, 4, 5 };

static关键字确保只初始化一次,并且是声明类型的一部分(而不是每个实例)。readonly关键字确保longByteArray变量之后不能被改变。

Definitions.longByteArray = new byte[] { 4, 5, 6 };   // Not possible.

警告:数组是可变的,所以在上面的代码中,我仍然可以这样做:

Definitions.longByteArray[3] = 82;                    // Allowed.

为了防止这种情况发生,不要使用数组类型,而是使用只读集合接口,例如IEnumerable<T>IReadOnlyList<T>,甚至更好的是使用只读集合类型,如ReadOnlyCollection<T>,它甚至不允许通过转换进行修改。

public static readonly IReadOnlyList<byte> longByteArray = new byte[] { 1, 2, 3, 4, 5 };

不错的回答,但是你的示例无法编译,因为 byte a = 82974; 是编译时错误。 - Alexei Levenkov
@AlexeiLevenkov 很好的发现。我只是需要想出一些随机数字而已。:P - Daniel A.A. Pelsmaeker
在最后一个使用IReadOnlyList的示例中,您仍然可以将其转换为byte[]并修改数组中的值。正如您可以在这里看到的那样。如果那是一个列表,您可以在末尾使用AsReadonly()。 - Joel McBeth
如果你感到疼痛,那就不要这么做。如果你在接口中进行显式强制类型转换,实际上就是在表明你知道自己在做什么并且愿意承担后果。我可以在将来的版本中更改底层实现,这样你的强制类型转换将会失败。 - Daniel A.A. Pelsmaeker
@Virtlink 根据这个逻辑,干脆就不要将其设置为只读,如果你正在修改它,那么你知道自己在做什么并接受后果。 - Joel McBeth
显示剩余2条评论

6

你不能创建一个const数组。根据文档

用户定义的类型,包括类、结构体和数组,不能是const

你需要将它声明为静态只读字段,像这样:

public static class Definitions
{
    public static readonly byte[] gLongByteArray = new byte[] { 
        1, 2, 3,
        //and so on
    };
}

当然,有人可能会在运行时覆盖您的数组元素,就像这样:
Definitions.gLongByteArray[0] = 0xFF; 

您需要使用内置的集合之一,如@Virtlink建议或创建自己的自定义只读数组类来防止这种情况发生 (示例)。


2
将所有内容写入文件并作为资源嵌入!

0

静态类将在首次加载应用程序时初始化。除非您已经明确测量到它是一个问题,否则不必担心性能。


4
从技术上讲,静态初始化发生在变量时间上,具体取决于何时以及如何引用类。这很少成为问题,但了解一下还是值得的。保证在引用该字段时至少已经运行了静态初始化。 - Dan Bryant

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