获取,设置和属性最佳实践。Java与C#


92

我现在正在上一门C#类,并且试图找出做事的最佳方法。我来自Java背景,因此只熟悉Java最佳实践。我是C#新手!

在Java中,如果我有私有财产,则可以这样做。

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

在C#中,我看到有很多方法可以做到这一点。

我可以像Java那样做:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

或者我可以这样:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

要么:

public string Name { get; set; }

我应该使用哪一种?每种方法有哪些警告或细微之处?创建类时,我遵循的是我从Java中学到的最佳常规(尤其是阅读Effective Java)。因此,例如,我赞成不变性(仅在必要时提供设置器)。我很好奇这些实践如何与C#中提供setter和getter的各种方式相适应;本质上,我如何将Java世界中的最佳实践转换为C#?

编辑

我将其发布为对乔恩·斯凯特(Jon Skeet)的回答的评论,但随后很长一段时间:

非平凡属性(例如,可能需要大量处理和验证)又如何呢?我是否仍可以通过公共属性公开它,但逻辑封装在get和中set?为什么要/应该通过拥有专用的setter和getter方法(以及相关的处理和验证逻辑)来做到这一点。

Answers:


88

C#6之前的版本

我会用其中的最后一个作为琐碎的属性。请注意,由于getter和setter都是公共的,因此我将其称为公共属性。

对于自动实现的属性,不变性会有些痛苦-您不能编写只有吸气剂的自动属性;您能找到的最接近的是:

public string Foo { get; private set; }

这并不是真正的不变……只是在课堂之外不变。因此,您可能希望使用一个真实的只读属性:

private readonly string foo;
public string Foo { get { return foo; } }

您绝对不想写getName()setName()。在某些情况下,编写Get / Set方法而不是使用属性是有意义的,特别是如果它们可能很昂贵,并且您希望强调这一点时,尤其如此。但是,您希望对方法使用PascalCase的.NET命名约定,而且您也不想使用常规方法来实现这种琐碎的属性-在这里,属性更为常见。

C#6

好了,我们终于有了适当的只读自动实现属性:

// This can only be assigned to within the constructor
public string Foo { get; }

同样,对于只读属性,其需要做一些工作,就可以使用会员浓郁的属性:

public double Area => height * width;

5
更准确地说:任何代码reiew都会指出Java方式是一种绕过有效语言AND(!)运行时构造并杀死该属性的使用权的黑客(即object.property =“ value”)。在某些团队中,这会引起关于态度的精彩讨论-取决于资历,并加上将这种态度用于竞争对手的动机。认真地,不要与语言对抗。尤其是因为Java“方式”是为了不修改语言以支持不动产而选择的一种。
TomTom

1
我想这是个好消息,我的回应似乎与您提到的内容并不矛盾。坏消息是你的手指比我的手指快得多。深刻的见解,并感谢您提供的详细信息。
jeremyalan 2011年

4
编辑您的答案:您可以将get / set方法与逻辑中的任意数量一起使用。我们经常这样做,尤其是对于验证。但是,最佳实践是在属性中不要有很多慢速逻辑(例如,数据库访问),危险逻辑(抛出异常)或突变(改变很多状态)。期望属性或多或少地像简单状态一样起作用。应该使用函数来指示更多内容。
CodexArcanum 2011年

2
@rtindru:是的,我知道这一点。完全有可能编写一个只读属性。但是,如果没有设置访问器,就无法声明自动实现的属性。
乔恩·斯基特

2
从C#6开始,您现在可以拥有没有设置访问器public int Y { get; } = y;)的自动属性
Scott Chamberlain 2015年

17

如果您只需要一个变量来存储一些数据:

public string Name { get; set; }

是否要使其显示为只读?

public string Name { get; private set; }

甚至更好...

private readonly string _name;

...

public string Name { get { return _name; } }

想要在分配属性之前进行一些值检查吗?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

通常,仅在某些情况下使用GetXyz()和SetXyz(),并且在感觉合适时只需使用直觉即可。总的来说,我希望大多数get / set属性不会包含很多逻辑,并且很少有意外的副作用。如果要读取属性值需要调用服务或从用户那里获取输入以构建我所请求的对象,则可以将其包装到方法中,并调用BuildXyz(),而不是GetXyz()


2
我不会在属性中引发异常,而宁愿使用带有合同的方法设置器来指定此类特定行为。如果一个属性的类型为int,我希望每个int都符合条件。在我看来,要求引发异常的事情并不简单,根据我的说法,INotifyPropertyChanged类型调用更多。
flindeberg 2012年

3
私人二传手!=不变
皮耶达

Piedar是对的。私有的setter只是意味着我不能进行分配,但是我仍然可以使用myList.Add(),例如(只要对象可以更改,就可以更改)。

1
@flindeberg对不起,但我不同意...如果您的进度条带有Min/ Max值,那该怎么办?您要确保Max > Min?有一个SetRange(Min, Max)可能有意义,但是那您将如何读回那些值?最小/最大只读属性?抛出无效输入异常似乎是处理此问题的最干净方法。
2016年

12

使用C#中的属性,而不是获取/设置方法。他们在那里是为了您的方便,这是惯用的。

至于您的两个C#示例,一个仅仅是另一个的语法糖。如果只需要一个简单的包装实例变量,则使用auto属性;如果需要在getter和/或setter中添加逻辑,请使用完整版本。


5

在C#中,倾向于使用属性公开用于get和/或set的私有字段。您提到的表单是一种自动属性,其中的get和set会自动为您生成一个隐藏的透视表支持字段。

我尽可能地支持自动属性,但永远不要在C#中执行set / get方法对。


5
public string Name { get; set; }

这只是一个自动实现的属性,在技术上与普通属性相同。编译时将创建一个后备字段。

最终所有属性都将转换为函数,因此最终的实际编译实现与Java中使用的相同。

当您不必在后备字段上执行特定操作时,请使用自动实现的属性。否则,请使用普通属性。当操作有副作用或计算量很大时,请使用get和set函数,否则请使用属性。


4

无论在C#中选择哪种方式,最终结果都是相同的。您将获得带有单独的getter和setter方法的backinng变量。通过使用属性,您将遵循最佳实践,因此这与您要获得的详细程度有关。

我个人会选择自动属性,最后一个版本:public string Name { get; set; },因为它们占用的空间最少。而且,如果需要添加诸如验证之类的功能,将来可以随时扩展它们。


4

只要有可能,我都会选择public,string Name { get; set; }因为它简洁且易于阅读。但是,有时可能需要这样做

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

2
您能否解释何时以及为何需要这种事情?
Jesper

现在,我想不出使用第二版的原因。我只说过“也许是必要的时候”。除非我很肯定,否则我不喜欢使用绝对值。
SquidScareMe 2011年

3
为什么是“ lolz”?您可以通过投票显示对评论的支持。
SquidScareMe 2011年

1
使用显式后备字段的原因之一是当您要针对该值执行原子/线程安全操作时
2016年

4

在C#中,首选方法是通过属性而不是getX()and setX()方法。另外,请注意,C#不需要属性同时具有get和set-您可以具有仅获取属性和仅设置属性。

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}

4

首先让我尝试解释一下您写的内容:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

如今,这种结构也被使用,但是如果您想做一些额外的功能,那么它是最合适的,例如,当设置了一个值时,您可以对其进行解析以将其大写并将其保存在私有成员中以供内部使用。

使用.net Framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

祝您在C#中好运


3

如前所述,所有这些方法都会产生相同的结果。最重要的是,您选择一个约定并遵守约定。我更喜欢使用最后两个属性示例。


2

像这里的大多数答案一样,请使用“自动”属性。直观,更少的代码行,更干净。如果您应该序列化类,请[Serializable]使用[DataConract]属性标记类/ 。如果您使用[DataContract]标记成员

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

私人或公共二传手取决于您的喜好。

另请注意,自动属性需要同时使用getter和setter(公共或私有)。

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

或者,如果只想获取/设置,请自己创建一个支持字段


0

我应该使用哪一种?每种方法有哪些警告或细微之处?

使用属性时,有一个需要注意的警告:使用属性,您不能对getter或setter进行任何参数化。

例如,假设您要检索列表项,并且还希望同时应用过滤器。使用get方法,您可以编写如下内容:

obj.getItems(filter);

相反,使用属性,您必须先返回所有项目

obj.items

然后在下一步中应用该过滤器,否则您必须添加专用属性,以显示通过不同条件过滤的项目,这很快会使您的API膨胀:

obj.itemsFilteredByX
obj.itemsFilteredByY

有时可能是令人讨厌的事情,是当您开始使用属性时,例如obj.items,后来发现需要getter或setter参数化,或者会使类API用户的工作变得容易。现在,您需要重写API并修改代码中访问该属性的所有这些位置,或者找到替代解决方案。相反,使用get方法,例如obj.getItems(),您可以简单地扩展方法的签名以接受可选的“配置”对象,例如,obj.getItems(options)而不必重写所有调用方法的位置。

话虽这么说,C#中的(自动实现的)属性仍然是非常有用的快捷方式(由于这里提到的各种原因),因为在大多数情况下可能不需要参数化,但这是需要注意的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.