只能设置带有getter的自动属性,为什么?


96

我创建了一个自动属性:

public int Foo { get; } 

这只是吸气剂。但是当我构建一个构造函数时,我可以更改值:

public MyClass(string name)
{
    Foo = 5;
}

即使这是仅获得的,为什么有可能呢?


它实际上没有使用setter(因为它没有一个)。它直接设置基础字段(对我们而言是隐藏的,这就是您需要使用属性名称的原因)
Dennis_E

14
如果永远无法初始化/设置属性,该属性有什么用?Yacoub Massad给出了完美的答案
Vikhram'1

Answers:


123

这是C#6的一项新功能,“仅Getter的自动属性”,也称为“只读属性的自动属性初始化程序”,在Mark MSDN杂志的文章“ C#:新的和改进的C#6.0”中进行了讨论。 Michaelis和在C#6.0语言规范草案中

只读字段的setter仅可在构造函数中访问,在所有其他情况下,该字段仍为只读且行为与以前相同。

这是一种方便的语法,可减少您需要键入的代码量并消除显式声明专用模块级别变量以保存该值的需要。

自从C#3中引入自动实现的属性以来,该功能就被认为很重要,因为可变属性(带有getter和setter的属性)比不可变属性(只有getter的属性)的编写速度更快,这意味着人们可以试图使用可变属性以避免必须为只读属性通常要求的后备字段键入代码。《 Microsoft C#编程指南》相关部分中有关于自动实现属性的更多讨论。

肖恩·塞克斯顿(Sean Sexton)撰写的博客文章“#1,207 – C#6.0 –只读属性的自动属性初始化程序”有一个很好的解释和示例,如下所示:

在C#6.0之前,如果需要只读(不可变)属性,通常会使用在构造函数中初始化的只读后备字段,如下所示。

public class Dog 
{
    public string Name { get; set; }

    // DogCreationTime is immutable
    private readonly DateTime creTime;
    public DateTime DogCreationTime 
    {
        get { return creTime; }
    }

    public Dog(string name)
    {
        Name = name;
        creTime = DateTime.Now;
    }
}

在C#6.0中,可以使用自动实现的属性来实现只读属性。您可以通过使用自动属性初始化程序来实现。结果比上面的示例要干净得多,在上面的示例中,我们必须显式声明一个后备字段。

public class Dog
{
    public string Name { get; set; }

    // DogCreationTime is immutable
    public DateTime DogCreationTime { get; } = DateTime.Now;

    public Dog(string name)
    {
        Name = name;
    }
}

更多细节也可以在GitHub上的dotnet Roslyn回购中找到

现在可以在不使用setter的情况下声明自动属性。

仅吸气剂自动属性的支持字段隐式声明为只读(尽管这仅对于反射目的很重要)。如上例所示,可以通过属性上的初始化程序对其进行初始化。此外,可以在声明类型的构造函数主体中将仅getter属性分配给该属性,这将导致将值直接分配给基础字段:

这是关于更简洁地表达类型,但是请注意,它还消除了可变类型和不可变类型之间的语言上的重要区别:仅当您愿意使您的类可变时,自动属性才是可用的简写形式,因此倾向于默认那太好了。现在,借助仅具有吸气剂的自动属性,竞争环境已在可变和不可变之间平衡。

并在C#6.0语言规范草案中(注意:就Microsoft而言,语言规范是最终的,但尚未被批准为EMCA / ISO标准,因此是“草稿”):

自动实现的属性

自动实现的属性(或简称自动属性)是具有分号的访问器主体的非抽象非外部属性。自动属性必须具有get访问器,并且可以选择具有set访问器。

当将属性指定为自动实现的属性时,该属性将自动提供隐藏的后备字段,并且实现访问器以对该后备字段进行读取和写入。如果自动属性没有设置访问器,则将后备字段视为只读(只读字段)。就像只读字段一样,也可以在封闭类的构造函数的主体中将仅吸气剂的自动属性分配给该属性。这样的分配将直接分配给属性的只读后备字段。

自动属性可以选择具有property_initializer,该属性将作为variable_initializer(变量初始化器)直接应用于后备字段。


1
这是愚蠢的设计。如果将属性设置为只读的位置,则应该是编译时错误。
Shiv

对我来说,很奇怪的是,CS0200 C# Property or indexer cannot be assigned to -- it is read only使用常规属性时仍然会出现编译器错误。是否将“仅Getter的自动属性”视为只读?
Kyle Delaney

24

这是C#6中的一项新功能,可让您创建只读属性并从构造函数初始化它们的值(或在声明它们时内联)。

如果您尝试在构造函数外部更改此属性的值,则会产生编译错误。

从某种意义上说,它是只读的,一旦初始化了它的值(内联或在构造函数内部),就无法更改其值。


那么,这是什么解释呢?吸气剂的定义是什么?
Noam B.

16

如果无法从构造函数(或自动属性初始化程序)初始化只读属性,则它将无用,因为它将始终返回其类型的默认值(对于数字为0,对于引用类型为null )。相同的语义应用于所有C#版本中的只读字段。

要定义一个真正的仅用于getter的属性(无法从构造函数初始化),您需要指定其返回的内容作为定义的一部分:

public int Foo { get { return 5; } }

或者,更简洁地在C#6中:

public int Foo => 5;

并非完全正确,只读属性对于在代码中封装某些条件非常有用。您可以编写一个if语句和几乎任何代码来评估其他属性或条件,并在每次读取该属性时返回一个适当的值
sebagomez

3
@sebagomez:我不确定我是否明白你的意思,这不是我在示例中所展示的吗?
道格拉斯

5

“只读自动实现的属性”

首先,我想澄清一下

public string FirstName { get; }

被称为“只读自动实现的属性”

为了验证这一点,您可以使用Visual Studio运行并检查以上代码。如果将语言版本从C#6.0更改为C#5.0,则编译器将引发以下异常: C#5中不提供“只读自动实现的属性”功能。请使用语言版本6或更高版本。

更改C#语言版本,请访问此处

现在我要问你的第二个问题

“这只是吸气剂。但是当我构建一个构造函数时,我可以更改值”

Microsoft在只读逻辑上引入了“自动实现的只读属性”。众所周知,C#1.0提供了关键字“只读”。我们使用“ readonly”关键字作为字段的修饰符,并且可以在声明时在同一类的构造函数中两种方式分配该字段

以相同的方式,可以通过两种方式分配“只读自动实现的属性”的值

Way1(在声明时):

public string FirstName { get; } = "Banketeshvar";

Way2(在同一类的构造函数中)

Person()
{
 FirstName  = "Banketeshvar";
}

纯只读属性

如果您正在寻找纯粹的只读属性,那么为此

public string FullName => "Manish Sharma";

现在您不能从构造函数分配“ FullName”属性的值。如果您尝试这样做,它将抛出以下异常

“无法将属性或索引器'Person.FullName'分配给它-只读”


2
请注意,FullName =>“ foo bar”将在每个时间进行评估。
juF19年

4

在C#3.0版本中,自动属性功能已添加到该语言中。它允许您定义不带任何后备字段的属性,但是仍然需要使用构造函数将这些自动属性初始化为非默认值。C#6.0引入了一项称为自动属性初始化器的新功能,该功能使您可以在没有如下构造函数的情况下初始化这些属性:

以前,如果要使用自动属性创建对象并将自动属性初始化为非默认值,则需要构造函数,如下所示:

public class MyClass
{
    public int Foo { get; }

    public Foo(int foo)
    {
        Foo = foo;
    }
}

现在在C#6.0中,能够将初始化程序与自动属性一起使用的功能意味着不需要显式的构造函数代码。

public string Foo { get; } = "SomeString";

public List<string> Genres { get; } = new List<string> { "Comedy", "Drama" };

您可以在这里找到更多信息


1

声明的变量readonly可以在构造函数中编写,但是使用支持该属性的语言,在构造函数返回后不能修改。提供该限定符是一种语言功能,因为字段的值通常会根据构造函数参数而变化(这意味着它们不能在构造函数开始之前初始化),但是不必在构造函数返回后进行更改,但这是必需的。仅适用于暴露为字段的变量。readonly限定字段的语义在许多情况下对于公共成员来说是完美的,除了类将成员(甚至是不可变成员)公开为属性而不是字段通常更好。

正如存在读写自动属性以允许类像普通字段一样容易地公开可变属性一样,存在只读自动属性以使类像readonly合格字段一样容易地公开不可变属性。就像readonly可以在构造函数中编写限定字段一样,使用仅获取属性也可以编写。

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.