为 C# 自动属性提供初始值的最佳方法是什么?

What is the best way to give a C# auto-property an initial value?

提问人:bentford 提问时间:9/3/2008 最后编辑:LopDevbentford 更新时间:7/5/2023 访问量:1235627

问:

如何为 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;
    }
}

有没有更好的方法?

C# 构造函数 getter setter automatic-properties

评论


答:

6赞 OwenP 9/3/2008 #1

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

2937赞 Darren Kopp 9/3/2008 #2

在 C# 5 及更早版本中,若要为自动实现的属性提供初始值,必须在构造函数中执行此操作。

C# 6.0 开始,可以内联指定初始值。语法为:

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

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

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

影响 IL 的属性示例包括 ThreadStaticAttributeCallerMemberNameAttribute、...

评论

13赞 Olivier Jacot-Descombes 2/21/2022
值得注意的是,这也适用于仅限 getter 的属性:public int X { get; } = x;
1赞 ATL_DEV 3/27/2023
只有二传手呢?
1赞 Olivier Jacot-Descombes 6/8/2023
@Narish,:仅当是常量、文本或只读字段时,您的版本才有效。否则,它将返回每次调用时可能更改的当前值。在我的版本中,可以是一个静态读写字段,即使字段更改,它也将始终返回相同的值。xxx
1赞 Olivier Jacot-Descombes 6/8/2023
@ATL_DEV,仅 setter 属性不能是 auto 属性。
1赞 Olivier Jacot-Descombes 6/9/2023
请参见:sharplab.io/#gist:a8cf845d585e24e4d4d1a5021ed863ad。右侧面板显示反编译的 IL 代码。这就是 C# 的真正作用。
76赞 crucible 9/3/2008 #3

有时我使用它,如果我不想让它实际设置并持久化在我的数据库中:

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

        set { _name = value; } 
    }
}

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

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

评论

38赞 abatishchev 8/9/2010
return _name ?? "Default Name";甚至可能更清楚你的
23赞 Sebastian Mach 12/16/2010
@abatishchev:虽然那不一样。如果字符串为 “” 或 null,则坩埚代码将返回“默认名称”,但使用您的方法仅在 null 时返回“默认名称”。此外,“??”还是“IsNullOrEmpty”更清楚是可以讨论的。
2赞 Jeremy Thompson 12/26/2020
该点是默认值,因此可为 null 的检查会破坏该点。Keith 的回答通过在 Ctor 中初始化它来证明这一点。如果是针对 dB,我真的看不出与拥有默认列值并使其成为非 null 列有太大区别,无论类字段的数量如何,这都会更有效。我不会投反对票,但敦促开发人员考虑这一点,而不是在您的属性程序中使用空/空检查。
2赞 Jeremy Thompson 12/26/2020
为了澄清每次调用类属性时,它都会执行 null/empty 检查,因为 dB 只会在 INSERT 或 UPDATE 上执行,这通常是 dB 工作的 20%。相反,可能每个字符串属性都有一个额外的调用,恕我直言,这是浪费 cpu 周期和糟糕的设计选择。另外,现在有 Null Ref 类型,因此处理可为 null 的更常见。
3赞 Joel Coehoorn 9/3/2008 #4

这已经很老了,我的立场也变了。我把原来的答案留给后代。


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

嗯。。。也许这将是它以后自己问题的主题

评论

13赞 David Reis 7/29/2009
在不破坏调用代码的情况下,无法将字段重构为 auto 属性。它可能看起来相同,但生成的代码不同。使用 auto 属性时,调用代码会在后台调用 get_propname 和 set_propname,而如果它是字段,它只是直接访问该字段。
1赞 Jacob Krall 11/7/2009
您不能跨 AppDomain 边界访问字段,只能访问属性或方法。
5赞 Chris Farmer 9/3/2008
@Joel:数据绑定和其他基于反射的工具通常需要属性而不是字段。
213赞 Keith 9/4/2008 #5

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

我认为这种语法是 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";
15赞 Lex 9/5/2008 #6

在 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

评论

1赞 Lex 2/28/2016
哇,这是过去的爆炸。我似乎记得这是基于对规范的阅读(此处的部分摘录:msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx )。考虑到版本(和 Roslyn)的时间和数量,情况很可能不再如此。尽管反参考将不胜感激。
1赞 Mark Brackett 2/28/2016
无论您是否使用初始值或在构造函数中赋值,都会自动进行默认赋值。在语义上略有不同 - 字段赋值发生在构造函数调用之前 - 但 null 赋值仍将发生。请参阅 10.4.5 “所有实例字段...首先初始化为默认值,然后执行实例字段初始值设定项“ msdn.microsoft.com/en-us/library/aa645757(VS.71).aspx
12赞 ghiboz 6/17/2010 #7

小完整示例:

using System.ComponentModel;

private bool bShowGroup ;
[Description("Show the group table"), Category("Sea"),DefaultValue(true)]
public bool ShowGroup
{
    get { return bShowGroup; }
    set { bShowGroup = value; }
}

评论

36赞 Boris B. 7/29/2011
那行不通。 只是一个序列化提示,它不会设置为,因为任何布尔值的默认值都是 .DefaultValueAttributeShowGrouptruefalse
0赞 Lance U. Matthews 9/28/2022
这不是自动属性。
0赞 Nag 5/27/2011 #8
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; }
}

评论

32赞 fire.eagle 5/27/2011
欢迎来到 Stack Overflow!正如您所知,除非您有一些好的新信息,否则像这样的老问题通常是不受欢迎的。但是,在本例中,其他几个人已经发布了有关 DefaultValue 属性的信息。如果其他人已经发布了您要说的内容,则通过单击其答案旁边数字上方的向上箭头来为他们投赞成票更合适。
11赞 Ben Voigt 6/3/2011
@fire:评论需要 50 声望。投票也需要声誉,IIRC。
330赞 Chuck Rostance 6/23/2011 #9

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; }
}

评论

6赞 Jason 7/6/2022
投票 -1:充其量,它看起来主观上看起来比在构造函数中初始化更整洁一些。这样做的代价是让刚接触代码库的开发人员感到困惑,性能更差,在语义上改变内置属性的含义,只允许常量,默认值很难在多个属性中发现,必须记住在每个构造函数重载中运行它,并且有可能在属性和构造函数中定义默认值。
0赞 Robert Harvey 7/5/2023
我更喜欢使用普通属性(而不是自动属性),在其中我可以设置私有变量的默认值,或者在构造函数中初始化属性,然后再求助于反射。
3赞 Zack Jannsen 7/31/2012 #10

澄清一下,是的,您需要在类派生对象的构造函数中设置默认值。您需要确保构造函数存在,并在使用时具有正确的构造访问修饰符。如果对象没有实例化,例如它没有构造函数(例如静态方法),则可以通过字段设置默认值。这里的理由是,对象本身只会创建一次,并且您不会实例化它。

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

基类的构造函数:

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

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

public SubClass() : base() { }

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

12赞 introspected 1/18/2013 #11

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

[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

评论

2赞 KinSlayerUY 11/1/2017
如上所述,使用反射来初始化默认值既缓慢又矫枉过正。在构造函数上初始化,使用非 auto 属性,或者在 c# 6 及更高版本上,使用已接受答案中显示的简化表示法
0赞 IEnjoyEatingVegetables 6/17/2022
什么时候使用它比此处所述的其他选项更好?
-4赞 user3076134 12/7/2013 #12

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

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

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

        SomeFlag = value;        
    }
}
45赞 Habib 4/29/2014 #13

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

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

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

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

请参见: C# 6:自动实现属性的第一反应、初始值设定项 - 作者:Jon Skeet

2赞 FloodMoo 9/5/2014 #14
public Class ClassName{
    public int PropName{get;set;}
    public ClassName{
        PropName=0;  //Default Value
    }
}

评论

0赞 Lance U. Matthews 9/28/2022
这在问题本身中已经得到了证明。
4赞 Preet Singh 1/11/2016 #15

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

7赞 RayLoveless 7/19/2016 #16

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

72赞 Shiva 7/26/2016 #17

在 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;
    }
}

评论

3赞 freefaller 4/20/2018
我还没有 C#6.0,并且正在检查自动属性的默认值需要什么版本。C# 6.0 是否也删除了设置值的需要,否则设置值将被编译器阻止?{ get; set; }{ get; private set; }
23赞 brakeroo 10/30/2016 #18

除了已接受的答案之外,对于要将默认属性定义为其他属性的函数的方案,可以在 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 上的详细信息

评论

0赞 Lance U. Matthews 9/28/2022
FullName是一个没有后备字段的计算属性,因此这并不能真正回答关于汽车属性的问题。
38赞 ANewGuyInTown 3/15/2017 #19

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

对于 Readonly 属性

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; 
 }

评论

8赞 Jeppe Stig Nielsen 7/13/2019
另外,考虑一个例子,其中两个属性和都有一个 getter,但 和的行为非常不同!class C { public DateTime P { get; } = DateTime.Now; public DateTime Q => DateTime.Now; }PQPQ
6赞 ComeIn 11/1/2018 #20
private string name;
public string Name 
{
    get 
    {
        if(name == null)
        {
            name = "Default Name";
        }
        return name;
    }
    set
    {
        name = value;
    }
}

评论

2赞 Jeppe Stig Nielsen 11/2/2018
我认为提问者想要一个自动属性,即类或结构中的非抽象属性,您只使用分号(通常与 结合使用)来指示编译器应该自动生成访问器的主体。get;set;get
9赞 SUNIL DHAPPADHULE 2/18/2019 #21

你可以简单地这样说

public sealed  class Employee
{
    public int Id { get; set; } = 101;
}
-1赞 Jesse Adam 5/25/2020 #22

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

//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";
}

评论

7赞 AjimOthy 7/10/2020
注意:这不会设置初始值,它会创建一个返回常量字符串的 get 访问器。如果 FuelUnits 的值设置为另一个字符串,则 FuelUnits 将忽略该值,并继续返回 get 中定义的文本字符串。
36赞 codez0mb1e 5/24/2021 #23

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

评论

7赞 Chris Schaller 10/19/2021
以前,我们可以只使用版本 9 实现,这对于增加您仍然可以设置值的范围更有用。这个答案表明我们必须等待 C# 9,这对于 OP 的预期行为来说是不正确的。public string Name { get; } = "Anonymous user";