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

如何为 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#5 和更早版本中,要为自动实现的属性提供一个初始值,您必须在构造函数中进行设置。

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

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

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

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

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

于 1/2/15 编辑

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

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

我认为这种语法是 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";

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

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

        set { _name = value; } 
    }
}

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

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

希望有帮助!

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

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

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

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

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

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

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

对于只读属性

public int ReadOnlyProp => 2;

对于可写和可读属性

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

在当前版本的C#(7.0)中 ,您可以执行以下操作:(该代码段显示的是,在与后备字段一起使用时,如何使用表达式绑定的获取 / 设置访问器使表达式更紧凑)

private string label = "Default Value";

// Expression-bodied get / set accessors.
public string Label
{
   get => label;
   set => this.label = value; 
 }

除了已经接受的答案外,对于要定义默认属性作为其他属性的函数的情况 ,您可以在 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 accessor 关键字。

有关MSDN 的详细信息

完整样本:

using System.ComponentModel;

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

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

[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