初始化C#自动属性

219

我习惯写这样的类:

public class foo {
  private string mBar = "bar";
  public string Bar {
    get { return mBar; }
    set { mBar = value; }
  }
  //... other methods, no constructor ...
}

将Bar转换为自动属性似乎很方便简洁,但是如果不添加构造函数并将初始化放在其中,如何保留初始化值呢?

public class foo2theRevengeOfFoo {
  //private string mBar = "bar";
  public string Bar { get; set; }
  //... other methods, no constructor ...
  //behavior has changed.
}

你可以看到,添加构造函数并不符合我应该从自动属性中获得的节省精力的要求。

像这样做对我更有意义:

public string Bar { get; set; } = "bar";

2
你不想在构造函数中完成它吗?因为在我看来,那似乎是最自然的地方。 - Garry Shutler
15
因为我以前不需要在构造函数中这么做,所以现在加入一个构造函数对我并没有任何帮助,也不能省去我的力气。 - dlamblin
6
如果它是“STATIC private string ...”,那么您不希望在构造函数中初始化,因为每次创建新对象时都会调用构造函数,这明确不是想要的。~~~ - Steve Pitchers
5
这个问题有点老,但是为了回复@StevePitchers和dlamblin,你可以创建一个静态构造函数来仅初始化你的静态属性一次... - Melvyn
@GarryShutler,我找到了一个不想在构造函数中初始化属性的有效理由:如果该属性标记为“virtual”并在派生类中被重写,则很可能需要不同的初始化逻辑。因为基础构造函数中的初始化代码调用属性设置器而不是直接设置后备字段,所以派生类必须避免那个“base()”构造函数,以避免触发默认的初始化逻辑。看起来像是一个棘手的问题。 - Steven Liekens
4个回答

258

更新 - 下面的回答是在C# 6之前编写的。在C# 6中,您可以编写:

public class Foo
{
    public string Bar { get; set; } = "bar";
}

你还可以编写只读的自动实现属性,在构造函数中只能写入(但也可以给定默认初始值):

public class Foo
{
    public string Bar { get; }

    public Foo(string bar)
    {
        Bar = bar;
    }
}

目前非常遗憾,没有方法可以做到这一点。你必须在构造函数中设置值。(使用构造函数链接可以帮助避免重复。)

自动实现的属性现在很方便,但肯定可以更好。我并不经常需要这种初始化,而是更喜欢只读自动实现的属性,它只能在构造函数中设置,并且由只读字段支持。

直到C# 5,这种情况都没有发生,但计划在C# 6中实现 - 既允许在声明时初始化,允许在构造函数体中初始化只读的自动实现属性。


33
这是VB.NET团队做得正确的地方之一 - 在VB中,您可以像这样声明并设置属性:Public Property MyProp As String = "asdf"。不幸的是,您无法像C#那样声明公共getter和私有setter。不确定为什么两个团队都无法正确实现此功能。 - mattmc3
5
他们的翻译几乎正确。由于某些原因,他们不允许初始化只读自动属性,尽管从语义上讲,它们比初始化读写属性更有意义。 - supercat
14
Jon Skeet已经写了一本关于C# 5.0的书,目前还未公开。三年后,Anders Hejlsberg将打开这本书,看看语言设计团队是否正确。 - Dmitry Fedorkov
5
@poke:已完成……尽管我不打算像这样更新所有我的回答 :) - Jon Skeet
1
Resharper给我提了这个建议。我说,好的,把它移到初始化器里面,然后我就找不到它了。呵呵。#程序员问题 - Yatrix
显示剩余3条评论

36
您可以通过类的构造函数来完成此操作:
public class foo {
  public foo(){
    Bar = "bar";
  }
  public string Bar {get;set;}
}

如果你有另一个构造函数(即,一个带参数的函数)或者一堆构造函数,你总是可以使用这个(称为构造函数链):

public class foo {
  private foo(){
    Bar = "bar";
    Baz = "baz";
  }
  public foo(int something) : this(){
    //do specialized initialization here
    Baz = string.Format("{0}Baz", something);
  }
  public string Bar {get; set;}
  public string Baz {get; set;}
}

如果您始终将调用链连接到默认构造函数,则可以在其中设置所有默认属性初始化。在链接时,链接的构造函数将在调用构造函数之前被调用,以便您的更专业的构造函数将能够根据适用情况设置不同的默认值。


1
嗯,我猜我确实展示了三种方法... :P - Aaron Powell
62
DefaultValueAttribute并不设置属性的值,它只是告诉Visual Studio默认值是什么,因此在属性窗口中,如果该值不是默认值,则会以粗体显示。它不会以任何方式更改该值。 - James Curran
10
+1 James >> MSDN:"DefaultValueAttribute 不会使成员自动使用该属性的值进行初始化。您必须在代码中设置初始值。" - Even Mien
示例有小修正;要从同一类中调用构造函数,请使用:"this()"而不是"<class name>()",如所示。尽管如此,这仍然是一个有帮助的答案。 - codenheim
6
这个答案应该更新,删除 DefaultValue 建议,因为它如展示的那样不起作用。 - ean5533
显示剩余4条评论

31

这将在C# 6.0中实现:

public int Y { get; } = 2;

3
在默认构造函数中(如果你还有其他非默认构造函数的话),请注意:
public foo() {
    Bar = "bar";
}

我相信这个代码不会比你原来的代码表现更差,因为这本来就是幕后发生的事情。

如果你有多个不同的构造函数,相比于仅初始化底层字段并不使用自动属性,这将是更多工作 - 我真的看不出在这种情况下你从自动属性中获得了什么收益... - Bittercoder
2
不过有一个区别:立即初始化意味着它甚至在默认构造函数之前就已经初始化了。最近我有一个应用程序,由于某些我不理解的奇怪原因,这实际上很重要。 - Michael Klement

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