C# getter和readonly的区别

41

以下两者有何区别?

class C
{
    // One:
    public static readonly int ValueAsAMember = 42;

    // Two:
    public static int ValueAsAProperty { get { return 42; } }
}

我习惯性地采用第一种方式来编写常量(除非它们是私有的/内部的,在这种情况下我使用const关键字),但最近看到了第二种形式。

在可读性、约定、性能或其他方面,两种方式是否存在优劣之分?


6
这可能不是一个适合在Stack Overflow上提出的好问题,但我还是会给出一个答案:使用const,这与您的第一行代码几乎相同。第二行引入了一个方法调用,虽然可能会被内联,但这有什么意义呢? - siride
3
为什么你不使用"const"? - briba
1
我对 readonly 的唯一用途是在构造函数中设置它们,通常是为了TDD和设计模式(WPF&SL)的接口。在此示例中使用 const 和C#命名标准,而不是c / c ++ 呵呵;) - Stígandr
9
const在IL中是在编译时被替换的。假设您有两个程序集A和B,程序集B引用程序集A的常量值。如果您更改了程序集A中的const值,但没有使用新编译版本更新程序集B,则可能会出现奇怪的行为,其中两个程序集之间的值不同。这绝对是一种边缘情况,但在设计公共API时需要考虑这个因素。 - Daniel Mann
2
siride - 为什么这个问题不适合在SO上提问? - jia103
9个回答

38

你有三种选择:

  • public static readonly int Value = 42;
  • public static int Value { get { return 42; } }
  • public const int Value = 42;

如果值在运行时不会改变,但可能在将来的代码版本中发生更改,请选择 static readonly

如果值可能在运行时更改,请选择属性。当然,如果您使用给定的代码,则值不会更改。

如果值确实是一个常量,即使在将来的版本中也不会改变(类似于 Math.PIint.MinValue),请选择const。当然,const的使用受值类型的限制。

conststatic readonly之间的区别在于,const值将在调用现场替换。如果您在将来的版本中更改了const的值,则所有依赖于您的类的程序集都需要使用新值重新编译。

属性需要方法调用(调用get方法是一种方法调用)。因此,如果值在运行时保持不变,则没有必要使用属性。


1
这意味着:不要将其选择为常量值。那么,为什么不能选择它? - Ben Aaronson
@BenAaronson:基本上是因为我以为方法调用会有一些开销。但重新思考后,我意识到调用将被内联。我已经删除了那个令人怀疑的建议。谢谢指出。 - pescolino
1
+1 对于“将来版本中也不会更改”的部分。太多程序员忘记了这个条件。 -1 对于“属性需要方法调用”的部分,因为编译器可以优化掉它。 - CodesInChaos

15

是的,有一个优点:

如果将来在任何时候(例如在代码的未来版本中)该值可以发生变化,以一种方式使其具有时间依赖性,您可以在只读属性中支持它,而不必更改类的公共接口。

如果您必须使用属性替换只读字段,则必须重新编译使用您的类的任何其他程序集。


3
请记住,readonly 字段只能在 classconstructor 中更改。 - Harry89pl
1
很好的观点;我没想到这一点。我很感激Pescolino的彻底回答以及Daniel Mann对于const关键字的回应。我故意没有问const的原因就是因为它不是讨论的一部分。感谢大家的回答! - jia103
只是一点注释:对于“只读”情况,如果您将来想要用另一个静态值更改该值,仍然可以顺利运行,而无需重新编译其他程序集。 - Lucas Trzesniewski
我认为它的缺点是你可以在不重新编译的情况下更改它。如果其他程序集像 var cache = SomeExpensivePureFunction(C.Value) 这样使用它会怎么样? - adrianm
1
@MarcosPereira:我同意,但在我看来,坚持永远不使用公共字段的原则的唯一实际原因就是我所描述的。至于自动属性,请注意,在撰写此问题及其答案时,只读自动属性尚不存在。 - O. R. Mapper
显示剩余7条评论

9

有两个主要的区别:

第一个区别是接口中不能有字段,但可以有属性。因此,如果您想在接口中使用此功能,则必须使用属性。

第二个更有趣的区别是,在构造对象时,readonly字段仍然可以被修改。看下面的代码:

public class MyTestClass
{
    public readonly int MyInt = 1;

    public MyTestClass()
    {
        MyInt = 2;
    }
}

如果一个调用者执行了

new MyTestClass().MyInt

对于静态readonly字段的静态构造函数也是如此,它们将得到2。


1
+1 提到接口。对于解耦实现非常有用。 - Alejandro

3
我个人认为,使用第一种方法更好地描述了值的意图 - 即它是不可变的。当一个人查看类的接口时,他会看到该值是只读的,并且不必担心它以后会被改变(因为在第二种情况下,他无法看到属性的实现)。
关于const声明的一个重要事项(我不认为readonly也是如此),即使你只是将值从42更改为41,更改字段的值也构成API更改。原因是对于consts,值是在编译时确定的,这意味着如果我编译一个使用您的常量的模块,而您稍后更改它,我仍然会使用旧值,直到我重新编译我的模块以使用您的新版本。

2

readonly 用于那些只能在构造函数中更改的事物上非常好用。例如,当您遵循TDD模式时,典型的服务接口就是这种情况。

在您的示例中,const 是最好的选择,毕竟它是一个常量。

readonly

const

祝好


1
我的主要优势在于使用readonly,你可以在代码的任何位置声明它。但是,你只有一次设置的机会。使用setter,你可以一次声明并设置。

一次性声明和设置? - Kellen Stuart

1

我认为第一行使用readonly关键字来使某个东西变成常量或只读。

而第二行则利用属性来实现只读。两者都可以实现相同的功能,但如果与IL进行比较,属性会在dll中添加一些额外的代码行。


0

是的,这两者之间有区别。

readonly 字段只能在构造函数中设置。

{get; private set;} 可以在类内任何时候进行设置。

示例:

public class Car
{
    public readonly string Name;

    public string color {get; private set;}

    public Car()
    {
        Name = "Car";
        Color = "Red";
    }

    // will fail compilation
    public void ModifyName()
    {
        Name = "Subaru"
    }

    // perfectly ok
    public void ModifyColor()
    {
        Color = "Green"
    }
}

"{ get; private set; }" 和 "{get;}" 不是同一件事。"{ get; private set; }" 表示该属性可以在类内部进行设置,但在类外部不可更改。"{get;}" 表示该属性仅可从构造函数中进行设置。它不能被类中的其他方法更改。它实际上是一个“只读”属性。 - ProgrammingLlama
@Llama,我修改了我的答案,删除了错误的部分。 - Kellen Stuart

-1
一个属性只是一个围绕着字段的“语法糖”,没有setter的属性只是一个声明为“readonly”的字段,因此编译器将允许您在构造函数中设置它,因为对于编译器来说,您正在引用一个“readonly”字段。关于使用字段或属性的更大讨论不在问题的范围内。是的,正是这种“语法糖”需要您重新编译,@SOreadytohelp所提到的。仅仅为了明确,属性是为其创建了get和set方法的字段,C#将允许您像引用字段一样引用它,而不必每次都调用getter或setter。

我该如何改进答案,以便更加清晰明了? - Zach Hutchins
你使用“field”和“property”这两个术语,好像它们不是同一个东西。 - Kellen Stuart
此外,这句话没有意义:“有一个更大的讨论围绕着使用字段或属性的问题,这不在问题的范围内。” - Kellen Stuart
@KolobCanyon 字段不是属性,字段只是属于对象或类的数据,属性基本上是 getter setter。就像我说的那样,它只是一个围绕字段(数据)的语法糖。属性没有与其关联的数据,因为它的本质只是一个包装器,通常围绕在字段周围(尽管这并非必须如此)。 - Zach Hutchins

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