2177

如何为 C# 自动属性赋予初始值?

我要么使用构造函数,要么恢复到旧语法。

使用构造函数:

class Person 
{
    public Person()
    {
        Name = "Initial Name";
    }
    public string Name { get; set; }
}

使用普通属性语法 (带有初始值)

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

有没有更好的办法?

4

23 回答 23

2767

在 C# 5 及更早版本中,要为自动实现的属性赋予初始值,您必须在构造函数中进行。

从 C# 6.0 开始,您可以在线指定初始值。语法是:

public int X { get; set; } = x; // C# 6 or higher

DefaultValueAttribute旨在供 VS 设计者(或任何其他消费者)用来指定默认值,而不是初始值。(即使在设计对象中,初始值也是默认值)。

在编译时DefaultValueAttribute不会影响生成的 IL,并且不会读取它以将属性初始化为该值(请参阅DefaultValue 属性不适用于我的 Auto Property)。

影响 IL 的属性示例有ThreadStaticAttribute, CallerMemberNameAttribute, ...

于 2008-09-02T21:46:23.683 回答
321

于 15 年 1 月 2 日编辑

C# 6

使用 C# 6,您可以直接初始化自动属性(终于!),现在还有其他答案可以描述这一点。

C# 5 及以下

尽管该属性的预期用途不是实际设置属性的值,但您可以使用反射来始终设置它们......

public class DefaultValuesTest
{    
    public DefaultValuesTest()
    {               
        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
        {
            DefaultValueAttribute myAttribute = (DefaultValueAttribute)property.Attributes[typeof(DefaultValueAttribute)];

            if (myAttribute != null)
            {
                property.SetValue(this, myAttribute.Value);
            }
        }
    }

    public void DoTest()
    {
        var db = DefaultValueBool;
        var ds = DefaultValueString;
        var di = DefaultValueInt;
    }


    [System.ComponentModel.DefaultValue(true)]
    public bool DefaultValueBool { get; set; }

    [System.ComponentModel.DefaultValue("Good")]
    public string DefaultValueString { get; set; }

    [System.ComponentModel.DefaultValue(27)]
    public int DefaultValueInt { get; set; }
}
于 2011-06-22T18:14:58.580 回答
198

当您内联变量的初始值时,无论如何它都会在构造函数中隐式完成。

我认为这种语法是 C# 中的最佳实践,最高可达 5:

class Person 
{
    public Person()
    {
        //do anything before variable assignment

        //assign initial values
        Name = "Default Name";

        //do anything after variable assignment
    }
    public string Name { get; set; }
}

因为这使您可以清楚地控制分配的顺序值。

从 C#6 开始,有一种新方法:

public string Name { get; set; } = "Default Name";
于 2008-09-04T12:16:06.860 回答
68

有时我会使用它,如果我不希望它被实际设置并保留在我的数据库中:

class Person
{
    private string _name; 
    public string Name 
    { 
        get 
        {
            return string.IsNullOrEmpty(_name) ? "Default Name" : _name;
        } 

        set { _name = value; } 
    }
}

显然,如果它不是字符串,那么我可以使对象为空(double?, int?)并检查它是否为空,返回一个默认值,或者返回它设置的值。

然后我可以在我的存储库中检查它是否是我的默认值并且不持久,或者在保存之前进行后门检查以查看支持值的真实状态。

希望有帮助!

于 2008-09-02T23:07:27.560 回答
65

在 C# 6.0 中,这是轻而易举的事!

您可以在Class声明本身的属性声明语句中执行此操作。

public class Coordinate
{ 
    public int X { get; set; } = 34; // get or set auto-property with initializer

    public int Y { get; } = 89;      // read-only auto-property with initializer

    public int Z { get; }            // read-only auto-property with no initializer
                                     // so it has to be initialized from constructor    

    public Coordinate()              // .ctor()
    {
        Z = 42;
    }
}
于 2016-07-25T19:35:56.847 回答
44

从 C# 6.0 开始,我们可以为自动实现的属性分配默认值。

public string Name { get; set; } = "Some Name";

我们还可以创建只读的自动实现属性,例如:

public string Name { get; } = "Some Name";

请参阅:C# 6:第一反应,自动实现属性的初始化程序 - 作者 Jon Skeet

于 2014-04-29T14:40:19.983 回答
35

C# (6.0) 及更高版本中,您可以执行以下操作:

对于只读属性

public int ReadOnlyProp => 2;

对于可写和可读属性

public string PropTest { get; set; } = "test";

在当前版本的C# (7.0)中,您可以执行以下操作:(该片段显示了如何使用表达式主体的 get/set 访问器来使与支持字段一起使用时更紧凑)

private string label = "Default Value";

// Expression-bodied get / set accessors.
public string Label
{
   get => label;
   set => this.label = value; 
 }
于 2017-03-15T01:07:28.783 回答
23

除了已经接受的答案之外,对于想要将默认属性定义为其他属性的函数的场景,您可以在 C#6.0(及更高版本)上使用表达式主体表示法以获得更优雅和简洁的构造,例如:

public class Person{

    public string FullName  => $"{First} {Last}"; // expression body notation

    public string First { get; set; } = "First";
    public string Last { get; set; } = "Last";
}

您可以通过以下方式使用上述内容

    var p = new Person();

    p.FullName; // First Last

    p.First = "Jon";
    p.Last = "Snow";

    p.FullName; // Jon Snow

为了能够使用上述“=>”表示法,该属性必须是只读的,并且您不要使用 get 访问器关键字。

MSDN上的详细信息

于 2016-10-30T02:16:41.230 回答
18

C# 9.0添加了init关键字的支持 - 非常有用且极其复杂的声明只读自动属性的方式:

宣布:

class Person 
{ 
    public string Name { get; init; } = "Anonymous user";
}

〜享受〜使用:

// 1. Person with default name
var anonymous = new Person();
Console.WriteLine($"Hello, {anonymous.Name}!");
// > Hello, Anonymous user!


// 2. Person with assigned value
var me = new Person { Name = "@codez0mb1e"};
Console.WriteLine($"Hello, {me.Name}!");
// > Hello, @codez0mb1e!


// 3. Attempt to re-assignment Name
me.Name = "My fake"; 
// > Compilation error: Init-only property can only be assigned in an object initializer
于 2021-05-24T14:13:58.850 回答
14

在 C# 6 及更高版本中,您可以简单地使用以下语法:

public object Foo { get; set; } = bar;

请注意,要拥有一个readonly属性,只需省略集合,如下所示:

public object Foo { get; } = bar;

您还可以readonly从构造函数分配自动属性。

在此之前,我的回答如下。

我会避免向构造函数添加默认值;将其留给动态分配,并避免有两个分配变量的点(即类型默认值和构造函数中)。通常,在这种情况下,我会简单地编写一个普通属性。

另一种选择是做 ASP.Net 所做的事情并通过属性定义默认值:

http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx

于 2008-09-04T17:08:08.763 回答
13

小完整样本:

using System.ComponentModel;

private bool bShowGroup ;
[Description("Show the group table"), Category("Sea"),DefaultValue(true)]
public bool ShowGroup
{
    get { return bShowGroup; }
    set { bShowGroup = value; }
}
于 2010-06-17T07:44:02.513 回答
12

我的解决方案是使用自定义属性,通过常量或使用属性类型初始化程序提供默认值属性初始化。

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class InstanceAttribute : Attribute
{
    public bool IsConstructorCall { get; private set; }
    public object[] Values { get; private set; }
    public InstanceAttribute() : this(true) { }
    public InstanceAttribute(object value) : this(false, value) { }
    public InstanceAttribute(bool isConstructorCall, params object[] values)
    {
        IsConstructorCall = isConstructorCall;
        Values = values ?? new object[0];
    }
}

要使用此属性,必须从特殊的基类初始化器继承一个类或使用静态辅助方法:

public abstract class DefaultValueInitializer
{
    protected DefaultValueInitializer()
    {
        InitializeDefaultValues(this);
    }

    public static void InitializeDefaultValues(object obj)
    {
        var props = from prop in obj.GetType().GetProperties()
                    let attrs = prop.GetCustomAttributes(typeof(InstanceAttribute), false)
                    where attrs.Any()
                    select new { Property = prop, Attr = ((InstanceAttribute)attrs.First()) };
        foreach (var pair in props)
        {
            object value = !pair.Attr.IsConstructorCall && pair.Attr.Values.Length > 0
                            ? pair.Attr.Values[0]
                            : Activator.CreateInstance(pair.Property.PropertyType, pair.Attr.Values);
            pair.Property.SetValue(obj, value, null);
        }
    }
}

使用示例:

public class Simple : DefaultValueInitializer
{
    [Instance("StringValue")]
    public string StringValue { get; set; }
    [Instance]
    public List<string> Items { get; set; }
    [Instance(true, 3,4)]
    public Point Point { get; set; }
}

public static void Main(string[] args)
{
    var obj = new Simple
        {
            Items = {"Item1"}
        };
    Console.WriteLine(obj.Items[0]);
    Console.WriteLine(obj.Point);
    Console.WriteLine(obj.StringValue);
}

输出:

Item1
(X=3,Y=4)
StringValue
于 2013-01-17T22:04:13.313 回答
8

你可以像这样简单地放

public sealed  class Employee
{
    public int Id { get; set; } = 101;
}
于 2019-02-18T15:10:30.953 回答
7

在构造函数中。构造函数的目的是初始化它的数据成员。

于 2016-07-18T16:39:49.720 回答
6
private string name;
public string Name 
{
    get 
    {
        if(name == null)
        {
            name = "Default Name";
        }
        return name;
    }
    set
    {
        name = value;
    }
}
于 2018-11-01T14:51:57.120 回答
5

您是否尝试过将DefaultValueAttributeShouldSerialize 和 Reset 方法与构造函数结合使用?如果您要创建一个可能显示在设计器表面或属性网格中的类,我觉得这两种方法中的一种是必要的。

于 2008-09-02T21:32:41.657 回答
4

使用构造函数,因为“当构造函数完成时,构造应该完成”。属性就像你的类所持有的状态,如果你必须初始化一个默认状态,你会在你的构造函数中这样做。

于 2016-01-11T11:33:09.490 回答
3

现在已经老了,我的立场已经改变了。我只为后代留下原始答案。


就个人而言,如果您不打算在自动属性之外做任何事情,我根本看不出将其作为属性的意义。只需将其保留为字段即可。这些项目的封装好处只是红鲱鱼,因为它们背后没有什么可以封装的。如果您需要更改底层实现,您仍然可以自由地将它们重构为属性,而不会破坏任何相关代码。

嗯......也许这将是稍后它自己的问题的主题

于 2008-09-03T03:43:20.533 回答
3

为了澄清,是的,您需要在构造函数中为类派生对象设置默认值。您将需要确保构造函数存在,并带有适当的访问修饰符,以便在使用时进行构造。如果对象没有被实例化,例如它没有构造函数(例如静态方法),那么默认值可以由字段设置。这里的原因是对象本身只会被创建一次,并且您不会实例化它。

@Darren Kopp - 好答案,干净,正确。重申一下,您可以为抽象方法编写构造函数。您只需要在编写构造函数时从基类访问它们:

基类的构造函数:

public BaseClassAbstract()
{
    this.PropertyName = "Default Name";
}

派生/具体/子类的构造函数:

public SubClass() : base() { }

这里的重点是,从基类中提取的实例变量可能会掩埋你的基字段名称。使用“this”设置当前实例化的对象值。将允许您根据当前实例和实例化它的所需权限级别(访问修饰符)正确地形成您的对象。

于 2012-07-31T11:02:41.160 回答
3
public Class ClassName{
    public int PropName{get;set;}
    public ClassName{
        PropName=0;  //Default Value
    }
}
于 2014-09-04T20:44:06.380 回答
1
class Person 
{    
    /// Gets/sets a value indicating whether auto 
    /// save of review layer is enabled or not
    [System.ComponentModel.DefaultValue(true)] 
    public bool AutoSaveReviewLayer { get; set; }
}
于 2011-05-26T19:29:05.667 回答
0

我知道这是一个老问题,但是当我在寻找如何拥有一个可以通过覆盖选项继承的默认值时出现了,我想出了

//base class
public class Car
{
    public virtual string FuelUnits
    {
        get { return "gasoline in gallons"; }
        protected set { }
    }
}
//derived
public class Tesla : Car
{
    public override string FuelUnits => "ampere hour";
}
于 2020-05-25T00:00:10.247 回答
-3

我认为这会为你提供一些默认值为 false 的 SomeFlag。

private bool _SomeFlagSet = false;
public bool SomeFlag
{
    get
    {
        if (!_SomeFlagSet)
            SomeFlag = false;        

        return SomeFlag;
    }
    set
    {
        if (!_SomeFlagSet)
            _SomeFlagSet = true;

        SomeFlag = value;        
    }
}
于 2013-12-06T21:50:59.703 回答