提问



如何为C#Auto-Property提供默认值?我要么使用构造函数,要么还原为旧语法。


使用构造函数:


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


使用普通属性语法(使用默认值)


private string name = "Default 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

其它参考1


编辑1/2/15


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

其它参考2


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


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

其它参考3


DefaultValueAttribute仅适用于vs设计器。它不会将属性初始化为该值。


请参见DefaultValue属性不适用于我的自动属性

其它参考4


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


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

        set { _name = value; } 
    }
}


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


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


希望有所帮助!

其它参考5


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


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


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


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


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

其它参考6


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

其它参考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; }
}

其它参考8


在 C#(6.0)&的版本中更大,你可以这样做:


对于Readonly属性


public int ReadOnlyProp => 2;


对于Writable和&可读属性


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

其它参考9


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


[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

其它参考10


除了已经接受的答案之外,对于您希望将默认属性定义为其他属性的函数的场景,您可以使用 表达式主体符号 在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的详细信息[33]

其它参考11


在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[34]

其它参考12


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

其它参考13


您是否尝试将DefaultValueAttribute或ShouldSerialize和Reset方法与构造函数一起使用?如果您正在制作一个可能出现在设计师表面或属性网格中的类,我觉得这两种方法中的一种是必要的。[35] [36]

其它参考14


public Class ClassName{
    public int PropName{get;set;}
    public ClassName{
        PropName=0;  //Default Value
    }
}

其它参考15


就个人而言,如果你不打算在自动财产之外做任何事情,我根本不明白把它变成财产的重点。把它留作田地吧。这些项目的封装优势只是红色的鲱鱼,因为封装后面没有任何东西。如果您需要更改底层实现,您仍然可以自由地将它们重构为属性而不会破坏任何相关代码。


嗯......也许这将成为后来问题的主题

其它参考16


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


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


基类的构造函数:


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


派生/混凝土/子类的构造函数:


public SubClass() : base() { }


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

其它参考17


使用构造函数,因为当构造函数完成时,应该完成构造。属性就像您的类所持有的状态,如果必须初始化默认状态,则可以在构造函数中执行此操作。

其它参考18


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

其它参考19


我认为这样做会让你的SomeFlag默认为false。


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

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

        SomeFlag = value;        
    }
}

其它参考20


在行中初始化,使用构造函数进行初始化是不好的做法,以后会导致更多的破坏性更改。