自动实现的 getter 和 setter 与公共字段

Posted

技术标签:

【中文标题】自动实现的 getter 和 setter 与公共字段【英文标题】:Auto-implemented getters and setters vs. public fields 【发布时间】:2010-09-11 19:20:59 【问题描述】:

我看到很多用于执行此操作的 C# 类的示例代码:

public class Point 
    public int x  get; set; 
    public int y  get; set; 

或者,在旧代码中,同样具有显式私有支持值且没有新的自动实现属性:

public class Point 
    private int _x;
    private int _y;

    public int x 
        get  return _x; 
        set  _x = value; 
    

    public int y 
        get  return _y; 
        set  _y = value; 
    

我的问题是为什么。执行上述操作与仅将这些成员设为公共字段(如下所示)之间是否有任何功能差异?

public class Point 
    public int x;
    public int y;

明确地说,当您需要对基础数据进行一些转换时,我理解 getter 和 setter 的价值。但在您只是传递值的情况下,这似乎是不必要的冗长。

【问题讨论】:

【参考方案1】:

它封装了这些成员的设置和访问。如果从现在开始,代码的开发人员需要在访问或设置成员时更改逻辑,则可以在不更改类协定的情况下完成。

【讨论】:

但这如何改变班级的合同呢?无论哪种方式,point.x = 1;point.x 都可以正常工作。 @Ajedi32 从技术上讲,他们不是同一份合同。如果将其从字段更改为属性(或将属性更改为字段),则必须重新编译访问该成员的任何内容。因为在访问属性和字段时,编译后的 IL 是不同的。【参考方案2】:

您永远不知道以后是否需要对数据进行一些翻译。如果你隐藏你的成员,你就做好了准备。如果您添加翻译,您班级的用户不会注意到,因为界面保持不变。

【讨论】:

【参考方案3】:

最大的区别在于,如果您更改了内部结构,您仍然可以按原样维护 getter 和 setter,更改它们的内部逻辑而不会伤害 API 的用户。

【讨论】:

【参考方案4】:

以后改成这样也简单多了:

public int x  get; private set; 

【讨论】:

我什至不知道你能做到这一点。 没错,这很有帮助,不幸的是,你不能在界面中这样做【参考方案5】:

我倾向于同意(这似乎是不必要的冗长),尽管这是我们团队尚未解决的问题,因此我们的编码标准仍然坚持所有类的详细属性。

Jeff Atwood 几年前处理过这个问题。他回顾性指出的最重要的一点是,从字段更改为属性是代码中的breaking change;任何消耗它的东西都必须重新编译才能使用新的类接口,所以如果你无法控制的任何东西正在消耗你的类,你可能会遇到问题。

【讨论】:

感谢该链接并让我相信在字段/属性切换中事情实际上会中断。这对我来说很奇怪,因为无论如何语法都是一样的。 你会认为它会编译成同样的东西,但显然内部机制略有不同。 在 C# 中,具有 get 和 set 的属性编译为一对方法,例如 get_PropertyName() 和 set_PropertyName(type value)。 C# 中的属性语法只是语法糖。属性可以执行代码,因此将它们编译为与字段相同的代码是没有意义的。 JIT 编译器确实优化了一个没有代码的属性以等同于一个字段,因此没有理由不使用 C# 3.0+ 中的简写语法的属性【参考方案6】:

如果在这种情况下您必须更改获取 x 和 y 的方式,您可以稍后添加属性。这是我觉得最令人困惑的地方。如果您使用公共成员变量,您可以稍后轻松地将其更改为属性,如果您需要在内部存储值,则可以使用名为 _x 和 _y 的私有变量。

【讨论】:

【参考方案7】:

这个想法是即使底层数据结构需要改变,类的公共接口也不必改变。

C# 有时会以不同的方式处理属性和变量。例如,您can't pass properties as ref or out parameters。因此,如果您出于某种原因需要更改数据结构并且您正在使用公共变量并且现在您需要使用属性,那么您的接口将不得不更改并且现在访问属性 x 的代码可能不再像它是变量时那样编译x:

Point pt = new Point();
if(Int32.TryParse(userInput, out pt.x))

     Console.WriteLine("x = 0", pt.x);
     Console.WriteLine("x must be a public variable! Otherwise, this won't compile.");

从一开始就使用属性可以避免这种情况,您可以根据需要随意调整底层实现,而不会破坏客户端代码。

【讨论】:

【参考方案8】:

AFAIK 生成的 CIL 接口不同。如果您将公共成员更改为属性,您正在更改它的公共接口并且需要重建使用该类的每个文件。如果您只更改 getter 和 setter 的实现,则不需要这样做。

【讨论】:

【参考方案9】:

在绑定和序列化方面,还需要考虑更改对公共成员的影响。这两者通常都依赖公共属性来检索和设置值。

【讨论】:

【参考方案10】:

也许只是公开字段,您可以引导您找到更多 Anemic Domain Model。

亲切的问候

【讨论】:

【参考方案11】:

Setter 和 getter 原则上是不好的(它们是一种糟糕的 OO 气味——我不会说它们是一种反模式,因为它们有时确实是必要的)。

不,从技术上讲没有区别,而且当我现在真的想共享对象的访问权限时,我偶尔会将其设为 public final 而不是添加 getter。

setter 和 getter 被“出售”的方式是,您可能需要知道某人正在获取值或更改值 - 这仅对原语有意义。

属性包对象(如 DAO、DTO 和显示对象)被排除在此规则之外,因为这些不是对象一词的真正“OO 设计”含义中的对象。 (您不会想到“向 DTO 或 bean 传递消息”——它们只是设计成一堆属性/值对)。

【讨论】:

【参考方案12】:

Setter 和 Getter 使您能够添加额外的抽象层,在纯 OOP 中,您应该始终通过对象提供给外部世界的接口来访问对象...

考虑一下这段代码,它将把你保存在 asp.net 中,如果没有 setter 和 getter 提供的抽象级别,这是不可能的:

class SomeControl


private string _SomeProperty  ;
public string SomeProperty 

  if ( _SomeProperty == null ) 
   return (string)Session [ "SomeProperty" ] ;
 else 
   return _SomeProperty ; 


【讨论】:

【参考方案13】:

由于自动实现的 getter 对属性和实际的私有存储变量采用相同的名称。以后怎么改?我认为要说的重点是使用自动实现而不是字段,以便将来如果您需要向 getter 和 setter 添加逻辑,则可以更改它。

例如:

public string x  get; set; 

例如,您已经多次使用 x 并且不想破坏您的代码。

您如何更改自动 getter 设置器...例如对于设置器,您只允许设置有效的电话号码格式...您如何更改代码以便只更改类?

我的想法是添加一个新的私有变量并添加相同的 x getter 和 setter。

private string _x;

public string x  
    get return _x; 
    set 
        if (Datetime.TryParse(value)) 
            _x = value;
        
    ; 

这就是你说的让它灵活的意思吗?

【讨论】:

【参考方案14】:

此外,您可以在 getter 和 setter 上设置断点,但不能在字段上。

【讨论】:

【参考方案15】:

还值得注意的是,您不能将自动属性设为只读,也不能内联初始化它们。这两个都是我希望在 .NET 的未来版本中看到的东西,但我相信你在 .NET 4.0 中都做不到。

这些天我唯一使用带有属性的支持字段的时候是当我的类实现 INotifyPropertyChanged 并且我需要在属性更改时触发 OnPropertyChanged 事件。

此外,在这些情况下,我在从构造函数传入值时直接设置支持字段(无需尝试触发 OnPropertyChangedEvent(此时无论如何都会为 NULL),在我使用属性本身的其他任何地方。

【讨论】:

这并不完全正确,如果您有 SomePropName get; private set;,然后使用对象初始化器设置属性..【参考方案16】:

为什么我们不只使用公共字段而不是使用属性呢 当我们不需要进行验证时调用访问器(get,set)?

    属性是提供灵活机制以只读或只写的成员 可以覆盖属性,但不能覆盖字段。

【讨论】:

【参考方案17】:

添加 getter 和 setter 使变量成为在 Wpf/C# 中工作的属性。

如果它只是一个公共成员变量,则无法从 XAML 访问它,因为它不是属性(即使它的公共成员变量也是如此)。

如果它有 setter 和 getter,那么它可以从 XAML 访问,因为它现在是一个属性。

【讨论】:

以上是关于自动实现的 getter 和 setter 与公共字段的主要内容,如果未能解决你的问题,请参考以下文章

带有 Getter/Setter 的私有列表与公共列表

Kotlin类与对象 ① ( 成员属性 | Kotlin 自动为成员字段生成 getter 和 setter 方法 | 手动设置成员的 getter 和 setter 方法 | 计算属性 )

getter和setter方法

具有公共getter的抽象属性,可以在具体类中定义私有setter吗?

不同的作用域属性 - 公共 getter 和内部 setter

公共字段与自动属性