C#自动属性-为什么我必须写“get; set;”?

52

如果在C#自动属性中,get和set都是必需的,那么为什么我还要费心指定"get;set;"呢?

9个回答

67

因为您可能需要一个只读属性:

public int Foo { get; private set; }

只写属性:

public int Foo { private get; set; }

3
你还可以添加各种修饰词,如protected等。 - Tigraine
是的,还有其他人说的...你需要一种区分字段和属性的方法。 - Brian Genisio
1
一个小的侧面说明:有只读字段的概念。框架将确保这些字段只被写入一次。这与私有设置器或获取器不同,如果您拥有访问权限,则可以写入它们。 - Cristian Libardo
3
@BrianGenisio 这些属性并不是真正的只读属性,而是写入私有属性,直到现在这种方式已经足够接近了。但是,在C# 6.0(Visual Studio 2015)中,我们拥有了真正的只读属性(如此回答)。耶! - Robert Synoradzki
@ensisNoctis 好的!从公共接口只读取,但你是正确的,它不是不可变的。 - Brian Genisio
显示剩余5条评论

58

错误:属性或索引器不能作为 out 或 ref 参数传递

如果你没有指定 {get; set;},那么编译器就不知道它是一个字段还是一个属性。 这很重要,因为虽然它们“看起来”相同,但编译器对它们的处理方式是不同的。例如,在属性上调用 "InitAnInt" 会引发错误。

class Test
{
    public int n;
    public int i { get; set; }
    public void InitAnInt(out int p)
    {
        p = 100;
    }
    public Test()
    {
        InitAnInt(out n); // This is OK
        InitAnInt(out i); // ERROR: A property or indexer may not be passed 
                          // as an out or ref parameter
    }
}

在类中,你不应该创建公共字段/变量,因为你永远不知道什么时候需要将其更改为具有get和set访问器,那么你就不知道会破坏哪些代码,特别是如果你有客户端对你的API进行编程。

此外,你可以为get和set使用不同的访问修饰符,例如{get; private set;}将get设置为public,将set设置为声明类的私有属性。


20

我想分享一下我在这个主题上的发现。

像下面这样编写属性,是 .net 3.0 中的一种快捷方式调用“自动实现属性”。

public int MyProperty { get; set; }

这样可以减少您的输入量。声明属性的长方式如下:

private int myProperty;
public int MyProperty 
{
  get { return myProperty; }
  set { myProperty = value; } 
}

当您使用“自动实现属性”时,编译器将生成连接get和set到一些“k_BackingField”的代码。下面是使用Reflector反汇编的代码。

public int MyProperty
{
    [CompilerGenerated]
    get
    {
        return this.<MyProperty>k__BackingField;
    }
    [CompilerGenerated]
    set
    {
        this.<MyProperty>k__BackingField = value;
    }
}

从IL反汇编出的C#代码

同时为getter和setter方法连接了一条线路。

[CompilerGenerated]
public void set_MyProperty(int value)
{
    this.<MyProperty>k__BackingField = value;
}
[CompilerGenerated]
public int get_MyProperty()
{
    return this.<MyProperty>k__BackingField;
}

从IL反编译出的C#代码

当您将 setter 设置为 private 时,声明只读自动实现属性:

 public int MyProperty { get; private set; }

编译器只会将"set"标记为私有。设置器和获取器方法是相同的。

public int MyProperty
{
    [CompilerGenerated]
    get
    {
        return this.<MyProperty>k__BackingField;
    }
    private [CompilerGenerated]
    set
    {
        this.<MyProperty>k__BackingField = value;
    }
}

从IL中反汇编的C#代码

所以我不确定为什么框架要求自动实现的属性同时有get和set。如果没有提供,它们本来可以不写set和setter方法的。但可能存在一些编译器级别的问题使得这很困难,我不清楚。

如果你看一下声明只读属性的长方式:

public int myProperty = 0;
public int MyProperty
{
    get { return myProperty; }
}  

然后看一下反汇编代码,setter根本不在那里。

public int Test2
{
    get
    {
        return this._test;
    }
}

public int get_Test2()
{
    return this._test;
}

从IL反汇编的C#代码


5
对于自动属性,私有的set方法是必需的,否则你将无法将值设为任何内容,这将是毫无意义的。对于非自动属性,你可以排除设置器,因为后备字段提供了一种在内部更改值的方式。 - Jeromy Irvine
好的观点,那是正确的。因为在使用自动实现属性时,如果您没有私有变量,就无法设置值(如果它不存在)。 - Ron Todosichuk

17

因为你需要一些方式来将其与普通字段区分开来。

此外,拥有不同的访问修饰符也很有用,例如:

public int MyProperty { get; private set; }

但是公开字段是无用的。 - Daniel Earwicker
同意。这种语法可能部分归因于Anders先生不想在语言中引入新的关键字。 - Cristian Libardo
1
Earwicker:我在我的Vector2f类中发现公共字段非常有用——它们比属性快得多,而且这个类足够简单,我永远不需要改变实现。 - Stefan Monov

5
编译器需要知道你是否想要生成getter和/或setter,或者是声明一个字段。

2

另外,因为自C# 6.0以来(在Visual Studio 2015中,在此回答发布时可用于Ultimate Preview版本),您可以实现真正的只读属性:

public string Name { get; }
public string Name { get; } = "This won't change even internally";

相较于当前不完美的解决方法——使用公共getter/私有setter配对,这种做法更好:
public string Name { get; private set; }

public Constructor() { Name="As initialised"; }
public void Method() { Name="This might be changed internally. By mistake. Or not."; }

以下是上述示例的编译和可执行版本(在线查看点击这里)。

using System;

public class Propertier {
    public string ReadOnlyPlease { get; private set; }

    public Propertier()  { ReadOnlyPlease="As initialised"; }
    public void Method() { ReadOnlyPlease="This might be changed internally"; }
    public override string ToString() { return String.Format("[{0}]",ReadOnlyPlease); }
}

public class Program {
    static void Main() {
        Propertier p=new Propertier();
        Console.WriteLine(p);

//      p.ReadOnlyPlease="Changing externally!";
//      Console.WriteLine(p);

        // error CS0272: The property or indexer `Propertier.ReadOnlyPlease' cannot be used in this context because the set accessor is inaccessible
        // That's good and intended.

        // But...
        p.Method();
        Console.WriteLine(p);
    }
}

关于C# 6.0的其他好消息,官方预览视频在这里


2
如果属性没有访问器,编译器将如何将其与字段分开?又有什么会将其与字段分开?

2
当然,你需要一种区分字段和属性的方法。但是必须使用关键字吗?例如,这两个声明明显不同:
public int Foo;
public int Bar { }

那样或许可行,也就是说,编译器可能理解这种语法。

但接下来你会遇到一个空块具有语义意义的情况。这似乎很危险。


2

既然没有人提到它... 你可以让自动属性成为虚拟的并重写它:

public virtual int Property { get; set; }

如果没有get/set,该如何覆盖它?请注意,您可以重写getter而不是setter:(参考链接)
public override int Property { get { return int.MinValue; } }

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