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

有更好的方法吗?


当前回答

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

[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

其他回答

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

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

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

在C#5和更早版本中,要给自动实现的财产一个初始值,必须在构造函数中完成。

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

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

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

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

影响IL的属性示例有ThreadStaticAttribute、CallerMemberNameAttribute。。。

private string name;
public string Name 
{
    get 
    {
        if(name == null)
        {
            name = "Default Name";
        }
        return name;
    }
    set
    {
        name = value;
    }
}