C# readonly与Get的区别

33

只读修饰符和只读属性之间有什么区别吗?

示例:

public class GetOnly
{
    public string MyProp { get; }
}

public class ReadOnly
{
    public readonly string MyProp;
}

奖励:有没有一种方法可以创建一个适用于两者的接口?(与泛型一起使用)

public interface ISomething
{
    public string MyProp { get; }
}

public class GetOnly : ISomething
{
    public string MyProp { get; }
}

public class ReadOnly : ISomething // Cannot implement
{
    public readonly string MyProp;
}

非常感谢您的提前帮助!


你可以在 ReadOnly 类上使用显式实现实现接口。 - Sir Rufo
4个回答

50
你基本上误解了这两个定义的含义。只暴露getter并不意味着值是只读的。
虽然在这个简单的例子中:
public class GetOnly
{
    public string MyProp { get; }
}

我们可以说MyProp永远不会改变其值,但我们并不能总是说只读属性的值不会被更改。这种情况的一个例子是我们无法看到GetOnly的实现,只知道它的公共定义 - 例如,如果您正在使用闭源第三方库。

一个更清晰的例子如下:

public interface ISomething
{
    string MyProp { get; }
}

这个接口并没有说MyProp是只读的,而是说无法更改属性。它并没有说明属性的行为。更糟糕的是,它只是说当显式转换为ISomething时,你不能更改属性。

完全有可能像这样实现接口(即使接口只公开了getter):

public class GetOnly : ISomething
{
    public string MyProp { get; set; }
}

readonly是一个修饰符,明确强制表明该值在声明或构造函数中以外永远不会改变(除非使用诸如反射之类的绕过方法)。

但是readonly不能用于属性,因为属性只是get/set方法的语法糖。另外,接口只定义方法,因此无法定义字段(以及只读字段)。

所以回答您的问题:是的,它们在很大程度上不同,只有表面相似。


24

乍一看,属性和字段在功能上是相等的,对于存储数据和传递数据的正常使用情况来说,它们之间没有太大的区别。

但你似乎已经发现了一个重要的问题:只有属性可以成为接口的一部分。

有没有办法使一个接口同时适用于两者?

没有。

此外,许多依赖反射(EF、序列化)的 API 特别寻找属性。


太好了!我正在查看哪一个在内存中更轻,并且遇到了这个问题,试图使代码更加DRY。 - robjam
@robjam如果你考虑使用只读变量而不是只读属性,那么你应该考虑根本不使用方法,而只需拥有一个静态void main(感受到讽刺了吗?)。只读属性是一个私有的只读字段和一个公共的void返回值。没有其他的。-http://tryroslyn.azurewebsites.net/#K4Zwlgdg5gBAygTxAFwKYFsB0BhA9gG31QGNkxcIRMBxVCVAJzGIG4AoUSWRFDTAGUgBHdp2jwkadOzYAHYACN8zGMXwBDECBjYYAbzZsYxmPKUrIyGAA99UVMhYBfNk6A== - Mafii
你忘记了两个重要点: 1)属性不仅可以是接口的一部分,也可以是基类的一部分,并且可以在从类到类的层次链中被覆盖。 2)只有属性可以在 WPF 绑定中用作源。 - Massimiliano Kraus
覆盖确实适用于只读内容。我可以在基类中创建一个只读的 { get; } 属性并将其设置为虚拟的。子类可以重写它,也许在自己的构造函数中分配不同的常量值,甚至改变整个逻辑,创建一个私有字段并执行以下操作: get { return _privateField; } 使该字段非只读,因此可以在类内部更改。你说的"WPF只是另一个API"是什么意思?它是在Windows中构建桌面应用程序的主要框架,所以这不是你可以轻易忽略的区别... o.O' - Massimiliano Kraus
使用getter的另一个小优点是,如果需要查看何时请求它,您可以轻松地在其上设置断点。虽然这只是一个小优点,但当我无法弄清楚如何添加自定义断点时,我曾经用过它。 - GilesDMiddleton

2

一个是字段(readonly),另一个是属性。接口不能定义字段,只能定义属性、方法、索引器和事件。

两者只能通过构造函数或字段初始化进行赋值,之后不能更改。


@HenkHolterman 我根据您的回复修改了我的答案(关于 private set)。谢谢! - CoolBots

2
在以下部分:
public class GetOnly
{
    public string MyProp {get; }
}

MyProp是一个属性。然而,在这部分中:

public class ReadOnly
{
    public readonly string MyProp;
}

MyProp 是一个 字段。这是两件不同的事情。

有没有一种方法可以创建一个同时适用于两者的接口?

没有。只有属性可以放入接口中,字段则不能。


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