如何为C#Auto-Property提供默认值?

ben*_*ord 1773 c# automatic-properties

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

使用构造函数:

class Person 
{
    public Person()
    {
        Name = "Initial Name";
    }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

private string name = "Initial Name";
public string Name 
{
    get 
    {
        return name;
    }
    set
    {
        name = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有更好的办法?

Dar*_*opp 2140

在C#5和更早版本中,要为自动实现的属性提供默认值,您必须在构造函数中执行此操作.

自C#6.0起包含自动属性初始值设定项的功能.语法是:

public int X { get; set; } = x; // C# 6 or higher
Run Code Online (Sandbox Code Playgroud)

  • 你错了.抽象类可以有构造函数. (105认同)
  • 抽象类通常具有您提供给它们的任何可访问性.如果您未指定构造函数,则始终具有默认的公共构造函数 (55认同)
  • 抽象类的构造函数永远不应该被赋予公共访问权限,因为它们永远不能被实例化.这样做会导致FxCop违规(希望您使用它)并违反.NET Framework设计指南 - http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613/ REF = sr_1_1?S =书籍&即= UTF8&QID = 1328710556&SR = 1-1 (38认同)
  • 实际上,抽象类通常具有受保护的构造函数. (28认同)
  • @HankSchultz不,对象初始化器只是编译器所做的糖.实际发出的是:`var person = new Person(); persion.Name ="My Value";`构造函数总是先运行 (18认同)
  • 值得注意的是,这也适用于仅 getter 属性:`public int X { get; } = x;` (14认同)
  • @CallMeLaNN在你的具体类中调用:base()来构造它的基础(抽象) (5认同)
  • 怎么样`[System.ComponentModel.DefaultValue(<YourDefaultValue>)]`????????? (5认同)
  • @DarrenKopp如果在非静态类中,你自己不编写任何实例构造函数,那么如果你的类是抽象的,那么自动生成的无参数实例构造函数将具有(1)`protected`可访问性,以及(2)`public否则就是可访问性. (4认同)
  • ComponentModel仅默认设计器或读取这些属性的东西的值,并为您初始化这些属性.您必须在构造函数中初始化属性.无论如何最好这样做. (2认同)
  • @Jespertheend编译器可能能够编译到4.0运行时,因为最终意图仍然存在(在构造函数中赋值).再一次,新语法只是您之前必须做的快捷方式. (2认同)

Chu*_*nce 287

编辑于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; }
}
Run Code Online (Sandbox Code Playgroud)

  • +1的聪明才智,但这可能真的很慢.想象一下,在内部循环中新建一堆项目而没有意识到构造函数使用反射... (26认同)
  • 你推荐的确很糟糕.它很慢,并且引入了非常简单的类的反射.如果它不是那么慢,可以使用某种类型的il-weaving与这样的属性(PostSharp,Fody等),但性能...... (25认同)
  • 投票-1:充其量,主观上看起来比在构造函数中初始化要整洁一些。这样做的代价是让刚接触代码库的开发人员感到困惑、性能更差、在语义上改变内置属性的含义、只允许常量、在多个属性中很难识别默认值、必须记住在每个构造函数重载中运行它,并且可以在属性和构造函数中定义默认值。 (6认同)
  • 是的,答案是:[System.ComponentModel.DefaultValue(GiveAnyTypeOfDefaultValueHere)] (3认同)
  • 如果基类使用静态构造函数将属性和值加载到字典中,则每个类都没有开销! (2认同)

Kei*_*ith 158

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

我认为这种语法是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; }
}
Run Code Online (Sandbox Code Playgroud)

这样可以清楚地控制分配的订单值.

从C#6开始,有一种新的方式:

public string Name { get; set; } = "Default Name";
Run Code Online (Sandbox Code Playgroud)

  • 如果构造函数需要以不同的方式(跨多个构造函数)进行初始化,那么这是最佳实践.如果他们都以相同的方式初始化,那么如果我还想要Person(字符串名称),那么你不是建议不好的做法,然后我添加属性int ID,我还有另一个构造函数Person(字符串名,int id)?违反开放延期,关闭修改.我认为,只要所有构造函数以相同的方式初始化成员,最佳实践就是在行初始化. (11认同)
  • @Keith我更喜欢`public Person():this("Default Name")`,而不是设置Name两次. (6认同)
  • @dbobrowski如果我有多个构造函数我会使用`public Person(string name,int id):this()`语法,但你有一个观点.这使您可以更精确地控制何时以内联选项更简单的可维护性为代价初始化属性. (2认同)

cru*_*ble 59

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

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

        set { _name = value; } 
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

希望有所帮助!

  • `return _name ?? "默认名称";`甚至可能更清楚你的 (31认同)
  • @abatishchev:虽然那不一样.如果字符串为""或者为null,则crucibles代码将返回"Default Name",但使用您的方法仅在它为null时才返回"Default Name".另外,可以讨论是否"??" 或"IsNullOrEmpty"更清楚. (15认同)
  • @phresnel:如果属性值可以是""并且它与null相同那么你就是对的.否则""是有意义的值,我的代码更好 (10认同)
  • 该点是默认值,因此可空检查会破坏该点。Keith 的回答通过在 Ctor 中初始化它来证明这一点。如果它是针对 dB 的,那么与使用默认列值并使其成为非空列相比,我并没有真正看到太大的区别,无论类字段的数量如何,这都会更有效。我不会投反对票,但敦促开发人员考虑这一点,而不是在您的财产程序中进行空/空检查。 (2认同)
  • 澄清一下,每次调用类属性时,它都会执行 null/empty 检查,而 dB 只会在 INSERT 或 UPDATE 上执行此检查,这通常是 dB 工作的 20%。相反,可能每个字符串属性都有一个额外的调用,恕我直言,这是浪费 CPU 周期,也是一个糟糕的设计选择。另外,现在有了空引用类型,因此处理可为空的类型更加常见。 (2认同)

Shi*_*iva 42

在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我还没有 C#6.0,正在检查自动属性的默认值需要什么版本。C# 6.0 是否也不再需要 `{ get; 放; }` 或 `{ 得到;私人套装;}` 否则设置值会被编译器阻止? (3认同)

Hab*_*bib 39

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

public string Name { get; set; } = "Some Name";
Run Code Online (Sandbox Code Playgroud)

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

public string Name { get; } = "Some Name";
Run Code Online (Sandbox Code Playgroud)

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


ANe*_*own 29

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

对于Readonly属性

public int ReadOnlyProp => 2;
Run Code Online (Sandbox Code Playgroud)

对于可写和可读属性

public string PropTest { get; set; } = "test";
Run Code Online (Sandbox Code Playgroud)

在当前版本的C#(7.0)中,您可以执行以下操作:(在使用支持字段时,该代码段显示如何使用表达式的get/set访问器使其更紧凑)

private string label = "Default Value";

// Expression-bodied get / set accessors.
public string Label
{
   get => label;
   set => this.label = value; 
 }
Run Code Online (Sandbox Code Playgroud)

  • 这很好,但只有三个例子中的第二个是自动属性. (4认同)
  • 同样,考虑一下示例`class C {public DateTime P {get; } = DateTime.Now; 公共DateTime Q =&gt; DateTime.Now; }`中的属性P和Q仅具有吸气剂,但是P和Q的行为却大不相同! (2认同)

bra*_*roo 17

除了已经接受的答案之外,对于您希望将默认属性定义为其他属性的函数的场景,您可以在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";
}
Run Code Online (Sandbox Code Playgroud)

您可以按以下方式使用上述内容

    var p = new Person();

    p.FullName; // First Last

    p.First = "Jon";
    p.Last = "Snow";

    p.FullName; // Jon Snow
Run Code Online (Sandbox Code Playgroud)

为了能够使用上述"=>"表示法,该属性必须是只读的,并且不使用get accessor关键字.

有关MSDN的详细信息


ghi*_*boz 11

little complete sample:

using System.ComponentModel;

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

  • 那不行.`DefaultValueAttribute`只是一个序列化提示,它不会将`ShowGroup`设置为`true`,因为任何布尔值的默认值都是`false`. (34认同)

int*_*ted 10

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

[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];
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用此属性,必须从特殊的基类初始化程序继承一个类或使用静态帮助程序方法:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

Item1
(X=3,Y=4)
StringValue
Run Code Online (Sandbox Code Playgroud)

  • 如上所述,使用反射来初始化默认值既慢又矫枉过正。在构造函数上初始化,使用非自动属性,或者在 c# 6 及更高版本上,使用接受的答案中显示的简化符号 (2认同)

Lex*_*Lex 9

在C#6及更高版本中,您只需使用以下语法:

public object Foo { get; set; } = bar;
Run Code Online (Sandbox Code Playgroud)

请注意,要使readonly属性只是省略该集合,如下所示:

public object Foo { get; } = bar;
Run Code Online (Sandbox Code Playgroud)

您还可以readonly从构造函数中指定自动属性.

在此之前,我回答如下.

我会避免在构造函数中添加默认值; 保留动态赋值,避免分配变量的两个点(即默认类型和构造函数).通常我只是在这种情况下写一个普通的属性.

另一个选择是执行ASP.Net所做的事情并通过属性定义默认值:

http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx


cod*_*b1e 9

C# 9.0添加了init关键字的支持- 声明只读自动属性非常有用和复杂的方法:

宣布:

class Person 
{ 
    public string Name { get; init; } = "Anonymous user";
}
Run Code Online (Sandbox Code Playgroud)

~享受~使用:

// 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
Run Code Online (Sandbox Code Playgroud)

  • 以前我们可以只使用`public string Name { get; } = "匿名用户";` 版本 9 的实现对于扩大您仍然可以设置值的范围更有用。这个答案表明我们必须等待 C# 9,这对于 OP 的预期行为来说是不正确的。 (7认同)

Owe*_*enP 6

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


Com*_*eIn 6

private string name;
public string Name 
{
    get 
    {
        if(name == null)
        {
            name = "Default Name";
        }
        return name;
    }
    set
    {
        name = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为提问者想要一个自动属性,即类或结构中的非抽象属性,您只需使用“get;”和分号(通常与“set;”组合)来指示编译器应该生成主体自动获取`get`访问器。 (2认同)

Sun*_*ule 6

你可以像这样简单地放置

public sealed  class Employee
{
    public int Id { get; set; } = 101;
}
Run Code Online (Sandbox Code Playgroud)


Ray*_*ess 5

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