C#静态类与结构体在预定义字符串方面的区别

61

我的一位同事在C#中创建了以下代码结构(示例代码已经简化)。他的目标是缩短代码中所有预定义字符串的表示。

public struct PredefinedStrings
{
    public const string VeryLongName = "Very Long Name";
    public const string AnotherVeryLongName = "Another Very Long Name";
    public const string TheLastVeryLongName = "The Last Very Long Name";
}

public static void MethodThatUsesTheNames()
{
    Console.WriteLine(PredefinedStrings.VeryLongName);
    Console.WriteLine(PredefinedStrings.AnotherVeryLongName);
    Console.WriteLine(PredefinedStrings.TheLastVeryLongName);
}
虽然看起来它对他很有效,但我一直在想他是否应该使用静态类而不是结构体,或者是否有更优雅的方法来实现这个问题。
那么,做这件事情的首选方式是什么?请解释原因。

8
夸赞你的同事!我的同事很少使用常量,我们的代码中到处都是字符串字面值!哈哈不过我认为静态类有点像标准。 - Steve Elmer
我也在考虑这样做,但感觉有些过度工程化了。通常我采用2/3次敲打和重构的方法,最初分散字符串字面量,然后一旦我需要在另一个方法或类中再次使用同样的字符串,我就将其提取到静态类中。虽然这可能有点繁琐。 - Andy
13个回答

58
使用结构(struct)的解决方案,无法阻止其他代码执行new PredefinedStrings(),这样做不会有任何坏处,但从语义上讲,允许这样做是令人困惑的。使用静态类,编译器将禁止为您创建实例。毋庸置疑,静态类是在框架中提供常量的首选方法。
编辑添加,我说第二部分时没有证据-我后来搜索并相对较快地找到了System.Net.Mime.DispositionTypeNamesSystem.Net.WebRequestMethods.Http

2
这可能是使用结构体让我一开始皱眉的原因,但我没有想到去检查“约定”。谢谢提醒。这个答案对于在结构体和静态类之间做出选择非常有用,可能已经足够完成工作了。不过,我还需要检查一下是否在这里使用资源文件更合适。 - Rob van Groenewoud
我接受这个答案,因为它似乎适合这种特定情况,由于字符串极不可能改变。使用资源文件虽然有趣,但目前会给当前代码增加太多复杂性。 - Rob van Groenewoud

32

为了国际化方便,我希望所有字符串都放在资源文件中而不是嵌入在代码中。这样可以通过一个静态类访问它们,并将其作为属性成员。


2
+1,这就是答案。不仅仅是为了国际化,而且使用资源文件可以让您在不重新构建整个应用程序的情况下更正错误或更新字符串。 - Joel Coehoorn
4
有趣,我早在这个回答之前就说过同样的话了...我的赞呢?? :P - KP.
4
在这种情况下,我们相当确信这些字符串不会改变,使用资源文件似乎有点过头了。一个静态类就可以胜任这个工作(目前来说)。不过我会记住这个方法,以备将来使用。谢谢! - Rob van Groenewoud
你不认为他们会改变。但这并不意味着他们不会。另外,第二部分是本地化(Localization)的内容。你难道认为你永远不需要做本地化吗? - Andy

27
除了static classstruct之外,为什么不考虑使用resource文件来存储常量字符串呢?可以很容易地通过SomeNamespace.ResourceName.KeyName访问它们,并且根据其在项目中的位置可以在需要时进行外部管理而无需重新编译...

1
检查时间戳,你晚了1分钟,但我会给你+1 :) - Andrew

14

简单的经验法则是:除非没有其他选择,否则永远不要使用结构体。

常量有一些缺点:

  • 只能使用简单类型(字符串、数字等)
  • 常量会注入到引用程序集中。如果重新编译带有常量的程序集但不重新编译使用常量的程序集,则会遇到问题。

我会这样编写你的代码(注意重命名重构):

public static class KnownNames
{
    public static readonly string VeryLong = "Very Long Name";
    public static readonly string AnotherVeryLong = "Another Very Long Name";
    public static readonly string TheLastVeryLong = "The Last Very Long Name";
}

4
为什么你用“static readonly”而不是“const”?使用“const”可以在编译时进行评估,这对于预期的用途是可以的。 - Rob van Groenewoud
2
我列出了const的缺点:只能使用简单类型和编译时值。如果你100%确定它不会改变(例如像Math.Pi这样的常量),那么就使用const。 - Konstantin Spirin
1
我了解你的意思,谢谢你详细解释。虽然我不希望这些值发生改变,但你永远无法预测有谁会在他看似无穷的智慧中决定对其进行更改;-) - Rob van Groenewoud
1
属性表达式必须是常量,供您参考。 - Mark Richman

6

这段代码在功能上没有问题。但从风格上来说,我同意使用静态类更好。静态类声明类型的意图是仅保存静态/常量数据。


6

看起来您正在寻找一个资源文件(.resx)。这是一个不错的地方来存储这样的字符串,将您的字符串抽象成一个.resx将使您更容易在未来本地化应用程序。 MSDN页面http://msdn.microsoft.com/en-us/library/1ztca10y.aspx可以提供更多信息。


5

静态类似乎是最好的选择,因为它是最标准和预期的。我认为结构体/常量版本可能会更快,但经过一些测试后发现并非如此。

以下是我的快速测试结果,包括长度、比较、连接、复制、索引和子字符串。它在未附加调试器的情况下在 .Net 4.0 下运行。

                                          .Net 3.0  .Net 4.0
static class with static read-only string:  0.217   0.364  seconds
static class with const            string:  0.211   0.361  seconds
struct       with static read-only string:  0.211   0.372  seconds
struct       with const            string:  0.214   0.371  seconds
Properties.Resources               string:  1.173   1.268  seconds

它们的性能几乎相同,除非使用资源文件,这会使程序变慢。虽然Properties.Resources速度较慢,但它未存储在可执行文件中,因此在某些情况下可能更合适。所以在我看来,可以使用“静态类”或Properties.Resources来存储字符串。


1
常量字符串和静态字符串在内存中处理方式相同,这就是为什么结果相等的原因。 - Dennis19901

5
ETA: 感谢Dennis19901的指点:由于这些字符串是const类型,它们实际上不会占用任何空间。因此,这个struct仅会消耗1个字节。
OLD AND BUSTED INFO: 不要忘记一个结构体大小应该大约为16个字节的建议。在32位系统中,这里已经有了4个System.String引用。如果字符串数量增加,最好使用静态类。

好观点。我记得早些时候读过这个,但很长一段时间以来都没有实践,因为我的大多数代码似乎不适合结构体 :-) - Rob van Groenewoud
1
这与常量字符串无关,因为它们并未包含在结构体本身中。代码示例中结构体的大小将仅为1,因为这是最小大小。静态成员和常量成员与结构体的大小无关。 - Dennis19901

2

如果结构体符合这个答案中详细说明的某些标准,通常会避免使用结构体。

由于你的同事不是在存储值,所以应该使用类而不是结构体。


1

我认为静态更好,以下是我的理由。如果这段代码存在于某个库中,另一段代码使用了这个库,如果常量字段的值发生变化,那么不仅需要重新编译这个库(显然),还需要重新编译使用这个库的代码。原因是编译器会在引用常量值的地方插入常量值。但如果使用静态,则不会有这个问题,因为你引用的是字段而不是


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