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

2320

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

有更好的方法吗?

23个回答

12

简单完整示例:

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
不行。DefaultValueAttribute 只是序列化提示,它不会将 ShowGroup 设置为 true,因为任何布尔值的默认值都是false - Boris B.
这不是自动属性。 - Lance U. Matthews

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

2
如上所述,使用反射来初始化默认值既慢又过度。在构造函数中进行初始化,使用非自动属性或在C# 6及以上版本中,使用已接受答案中显示的简化符号。 - KinSlayerUY
何时最好使用这个选项而不是此处列出的其他选项? - IEnjoyEatingVegetables

9
你可以像这样简单地放置。
public sealed  class Employee
{
    public int Id { get; set; } = 101;
}

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

6

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

2
我认为提问者想要一个自动属性,即在类或结构中使用get;和分号(通常与set;组合使用)来表示编译器应自动生成get访问器的主体的非抽象属性。 - Jeppe Stig Nielsen

4

使用构造函数是因为“当构造函数完成时,构造应该完成”。属性就像类持有的状态一样,如果您需要初始化默认状态,则可以在构造函数中执行。


3
为了澄清,是的,你需要在派生类对象的构造函数中设置默认值。你需要确保构造函数以正确的访问修饰符存在于使用处。如果未实例化对象,例如没有构造函数(例如静态方法),那么可以通过字段设置默认值。这里的推理是,对象本身只会被创建一次,而不需要实例化它。
@Darren Kopp - 很好的答案,干净且正确。再次强调,你可以为抽象方法编写构造函数。你只需要在编写构造函数时从基类中访问它们:
基类中的构造函数:
public BaseClassAbstract()
{
    this.PropertyName = "Default Name";
}

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

public SubClass() : base() { }

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

3

这篇文章已经过时了,我的立场也发生了变化。我只是为了纪念保留原始答案。


个人认为,如果除了自动属性之外你什么都不做的话,将其设置为属性就没有意义了。直接将其作为字段留下即可。对于这些项的封装好处只是误导,因为它们背后没有任何东西可以封装。如果您需要更改底层实现,仍然可以自由地将其重构为属性,而不会破坏任何依赖代码。

嗯...也许这将成为另一个问题的主题。


5
数据绑定和其他反射工具通常希望使用属性而不是字段。 - Chris Farmer
13
如果将一个字段重构为自动属性,就会破坏调用代码。尽管它看起来相同,但生成的代码是不同的。对于自动属性,调用代码在幕后调用get_propname和set_propname,而如果是字段,则直接访问该字段。 - David Reis
1
你不能跨越AppDomain边界访问一个字段,只能访问属性或方法。 - Jacob Krall

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

这已经在问题本身中得到了展示。 - Lance U. Matthews

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接