为什么不建议使用仅设置属性?


9

今天,在工作中,我的一位同事审查了我的代码,并建议我删除仅设置属性,而改用一种方法。

当我们俩都忙于其他事情时,他告诉我看Property Design《框架设计指南》一书中的部分。在书中,作者只是说要避免:

setter的属性比getter具有更广泛的可访问性

现在,我想知道为什么不建议使用仅设置属性?有人可以帮我澄清一下吗?


6
您能描述一下您认为仅设置属性合适的情况吗?这可能会使答案更有意义。
JohnFx

1
我正在尝试一个在语义上有意义的示例。唯一想到的是类的Password属性User。您可以设置它,但无法获取它。然后,您可以拥有一个只读HashedPassword属性。调用set将进行哈希处理并更改HashedPassword属性。如果你那样做,我不会对你大喊大叫。
斯科特·惠特洛克

Answers:


15

我认为这可能与期望有关。仅设置属性并不常见,并且通常将这些属性用于“哑”设置,仅用于存储值而无需进行大量处理。如果您在二传手中做大量工作,最好使用一种方法-人们期望方法可能需要很长时间才能执行并且可能会产生副作用。在属性中实现类似的行为可能会导致代码违反预期。

这是Microsoft财产使用指南的相关部分:

属性与方法

类库设计人员通常必须在将类成员实现为属性或方法之间做出决定。通常,方法代表动作,属性代表数据。使用以下准则可以帮助您在这些选项之间进行选择。

  • 当成员是逻辑数据成员时,请使用属性。在以下成员声明中,Name是属性,因为它是类的逻辑成员。
public string Name
{
    get 
    {
        return name;
    }
    set 
    {
        name = value;
    }
}

在以下情况下使用一种方法:

  • 该操作是一种转换,例如Object.ToString
  • 该操作非常昂贵,以至于您希望与用户进行交流,使他们应该考虑将结果缓存。
  • 使用get访问器获取属性值将产生明显的副作用。
  • 连续两次呼叫成员会产生不同的结果。
  • 执行顺序很重要。请注意,类型的属性应该能够以任意顺序设置和检索。
  • 该成员是静态的,但返回可以更改的值。
  • 成员返回一个数组。返回数组的属性可能会引起误解。通常,必须返回内部阵列的副本,以便用户无法更改内部状态。这加上用户可以轻松地认为它是索引属性的事实,导致代码效率低下。在下面的代码示例中,每次对Methods属性的调用都会创建该数组的副本。结果,将在以下循环中创建2 ^ n + 1个数组副本。
Type type = // Get a type.
for (int i = 0; i < type.Methods.Length; i++)
{
   if (type.Methods[i].Name.Equals ("text"))
   {
      // Perform some operation.
   }
}

[...跳过较长的示例...]

只读和只写属性

当用户无法更改属性的逻辑数据成员时,应使用只读属性。不要使用只写属性。


是的-最小惊奇原则在这里起作用。
Paul Butcher

6

因为在大多数情况下,这根本没有意义。您可能拥有哪些属性可以设置但无法读取?

如果OO是为了更好地表示现实世界,则set set属性可能表明您的建模还不错。

编辑:另请参见:https : //stackoverflow.com/questions/4564928/are-set-only-properties-bad-practice基本上说这是不直观的,并且set only属性基本上是另一个名称的方法,因此您应该使用方法。


1
我以前使用过仅设置属性。他们写入对象的私有字段以配置其行为。当外部代码不需要知道当前值,但是可能需要更改它时,它们很有用。当然,这种情况很少见,但是我已经看到了。
梅森惠勒

@Mason-我绝对不会说你永远不要使用它们,但是它们本质上应该是例外而不是规则。
乔恩·霍普金斯

@MasonWheeler不是Foo Foo { private get; set; }吗?我不会只写
这篇文章

6

好吧,我想像一下,如果您可以在某个对象上设置属性但从不获取它,那么您将永远不会知道其他对象是否会更改/覆盖您设置的值。如果您依赖设置的值,并且(由于某种原因)无法将其保留到想要获取的时间,那可能是个问题。

对于用户而言,使用方法而不是仅设置属性将减少混乱。方法的名称通常表示set-get-,但是属性名称通常不表示只能设置而不可以获取某些内容。我想,如果该属性类似于“ ReadOnlyBackgroundColour”,则不会对其他编码人员造成混淆,但这看起来很奇怪。


我同意,但是在这种情况下,setter方法有什么不同?
特拉维斯·克里斯蒂安

1
@Travis Christian:听起来OP正在使用一个setter但没有getter的情况下工作。这样他们可以设置一些东西,但是他们永远不会知道以后是否会改变。
FrustratedWithFormsDesigner

@Frustrated但是他们也永远不会知道是否使用方法再次更改了某些内容。
亚当李尔

@Anna Lear♦:如果有一个吸气剂,那么如果您的代码中有一个点可能突然怀疑所设置的值,则至少可以在使用该值之前对其进行测试。
FrustratedWithFormsDesigner

3
@沮丧我同意。只是问题是关于使用set-only属性与使用方法来完成相同的事情。
亚当李尔

-1

这是一个非常古老的话题,但是在这个后期阶段已经进入了我的视野,当我试图为只写属性做一个案例时,我想发表一些评论...

我有一组ActiveReport类,这些类是网站的一部分,这些类在许多用户选择后都被实例化并在回发上运行。

VB代码如下所示:

  Public Class SomeReport
    Private greader As New GenericReporting.CommonReader("AStoredProcedure", 
                                      {New SqlParameter("budget_id", 0)})

    Public WriteOnly Property BudgetID As Integer
      Set(value As Integer)
        greader.Parameters("budget_id").Value = value
      End Set
    End Property

    Public Sub New(Optional budget_id As Integer = 0)
      ' This call is required by the designer.
      InitializeComponent()

      ' Add any initialization after the InitializeComponent() call.
      BudgetID = budget_id
    End Sub
  End Class

这些报告使用通用胆量,CommonReader存储过程和默认值SqlParameters 的数组,每个都有一个关联的WriteOnly属性,根据报告设计的不同,这些属性可以在实例化时作为参数传递,或在实例化之前由用户设置调用报表Run方法。

  '''''''''''''''''''''''
  ' Parameter taken from a user selected row of a GridView
  '
  Dim SomeBudgetID As Integer = gvBudgets.SelectedDataKey.Values(budget_id)

  '''''''''''''''''''''''      
  ' On Instantiation
  '
  Dim R as ActiveReport = New SomeReport(SomeBudgetID)
  R.Run()

  '''''''''''''''''''''''      
  ' Or On Instantiation using "With" syntax
  '
  Dim R as ActiveReport = New SomeReport() With {.BudgetID = SomeBudgetID}
  R.Run()

  '''''''''''''''''''''''
  ' Or After
  '
  Dim R as ActiveReport = New SomeReport()
  R.BudgetID = SomeBudgetID
  R.Run()

因此,正如我所见,在这种情况下具有只写属性

  1. 允许更强大的类型检查,因为SqlParameters是泛型的
  2. 创建报告具有更大的灵活性,如果所有参数都可用,则可以立即实例化该报告,也可以在所有参数可用后再添加。
  3. 属性在实例化时支持“ With”语法
  4. 因为用户知道参数并且报告未更改参数,“ getter”是否真的必要?
  5. 由于SqlParameters是类而不是原始值,因此WriteOnly属性允许使用更简单的接口来设置参数

这就是我的想法。

我可以将其转换为方法吗?可以,但是界面似乎...不太好

  R2.BudgetID = SomeBudgetID

  R2.SetBudgetID(SomeBudgetID)
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.