Как вы даете C-Auto-свойство значение по умолчанию?

1568

Как вы даете С# 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;
    }
}

Есть ли лучший способ?

  • 47
    Это вводит в заблуждение, даже в IntelliSense говорится: «Устанавливает значение по умолчанию для свойства», но в MSDN говорится: «DefaultValueAttribute не будет вызывать автоматическую инициализацию элемента со значением атрибута. Вы должны установить начальное значение в твой код. ", Ург!
  • 19
    Это одно из немногих мест, где vb.net имеет явный верх, они могут устанавливать значения по умолчанию для свойств в определении.
Показать ещё 4 комментария
Теги:

23 ответа

1805
Лучший ответ

В С# 5 и более ранних версиях, чтобы дать автоматически реализованным свойствам значение по умолчанию, вы должны сделать это в конструкторе.

Возможность иметь инициализаторы свойств auto включена с С# 6.0. Синтаксис:

public int X { get; set; } = x; // C# 6 or higher
  • 1
    Я не могу сделать это с абстрактным классом. Мой абстрактный класс с авто-свойством кажется невозможным назначить свойство по умолчанию, так как абстрактный не может иметь конструктор.
  • 100
    Вы не правы. абстрактные классы могут иметь конструкторы.
Показать ещё 15 комментариев
245

Отредактировано 1/2/15

С# 6:

С С# 6 вы можете инициализировать авто-свойства напрямую (наконец-то!), Теперь в ветке есть другие ответы, которые описывают это.

С# 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; }
}
  • 3
    Да, это ответ: [System.ComponentModel.DefaultValue (GiveAnyTypeOfDefaultValueHere)]
  • 0
    Это здорово! Я бы изменил открытый конструктор на частный, а другие конструкторы связали бы с частным.
Показать ещё 8 комментариев
143

Когда вы встраиваете начальное значение для переменной, оно будет выполняться неявно в конструкторе.

Я бы сказал, что этот синтаксис был наилучшей практикой в ​​С# до 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; }
}

Так как это дает вам четкое управление значениям порядка.

С С# 6 существует новый способ:

public string Name { get; set; } = "Default Name"
  • 11
    Лучше всего, если конструкторы должны инициализироваться разными способами (через несколько конструкторов). Если все они инициализируются одинаково, разве вы не предлагаете плохую практику, если я также хочу Person (строковое имя), а затем позже я добавляю свойство int ID и у меня есть еще один конструктор Person (строковое имя, int id)? Нарушение открытого для расширения, закрытого для модификации. Я утверждаю, что лучше всего инициализировать в строке, если все конструкторы инициализируют элемент одинаково.
  • 2
    @dbobrowski, если бы у меня было несколько конструкторов, я бы использовал public Person(string name, int id) : this() синтаксис public Person(string name, int id) : this() , но у вас есть точка зрения. Это дает вам более точный контроль того, когда именно свойства инициализируются, за счет упрощения обслуживания встроенного параметра.
Показать ещё 2 комментария
73

DefaultValueAttribute ТОЛЬКО работает в дизайнере vs. Он не будет инициализировать свойство для этого значения.

См. Атрибут DefaultValue не работает с моим Автообъектом

  • 7
    +1: [DefaultValue] используется, чтобы сообщить разработчикам, каково значение по умолчанию. Если в окне свойств вы установите значение по умолчанию, то значение не будет записано в сгенерированный код. Аналогично, значения не по умолчанию отображаются жирным шрифтом в окне свойств. Конечно, это работает, только если [DefaultValue] действительно имеет правильное значение. Это ничего не делает для обеспечения этого.
  • 8
    DefaultValueAttribute не только для дизайнера. Однако «значение по умолчанию» не совпадает с «начальным значением».
52

Иногда я использую это, если я не хочу, чтобы он действительно был установлен и сохранялся в моем db:

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

        set { _name = value; } 
    }
}

Очевидно, если это не строка, я могу сделать объект нулевым (double?, int?) и проверить, не null ли он, вернуть значение по умолчанию или вернуть значение, которое оно установило.

Затем я могу сделать чек в моем репозитории, чтобы увидеть, если он мой по умолчанию и не сохраняется, или сделать проверку бэкдор, чтобы увидеть истинный статус резервного значения, прежде чем сохранять.

Надеюсь, что это поможет!

  • 30
    return _name ?? "Default Name"; наверное даже понятнее что твой
  • 15
    @abatishchev: хотя это не то же самое. Код Crucibles будет возвращать «Имя по умолчанию», если строка «» или ноль, но при использовании вашего подхода будет возвращено «Имя по умолчанию», только если оно нулевое. Кроме того, это дискуссионное ли "??" или "IsNullOrEmpty" более понятно.
Показать ещё 3 комментария
31

Начиная с С# 6.0, мы можем назначить значение по умолчанию для автоматически реализованных свойств.

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

Мы также можем создать автоматическое свойство, доступное только для чтения, например:

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

Смотрите: С# 6: первые реакции, инициализаторы для автоматически реализованных свойств - Jon Skeet

29

В С# 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;
    }
}
  • 1
    У меня пока нет C # 6.0, и я проверял, какая версия мне нужна для значений по умолчанию в авто-свойствах. В C # 6.0 также устраняется необходимость иметь { get; set; } или { get; private set; } как иначе установка значения будет заблокирована компилятором?
17

В версии С# (6.0) и выше вы можете:

Для свойств Readonly

public int ReadOnlyProp => 2;

Для обоих свойств Writable и Readable

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

В текущей версии С# (7.0) вы можете сделать: (фрагмент скорее показывает, как вы можете использовать выражения с расширенными функциями get/set, чтобы сделать более компактным при использовании с полями поддержки)

private string label = "Default Value";

// Expression-bodied get / set accessors.
public string Label
{
   get => label;
   set => this.label = value; 
 }
  • 2
    Это хорошо, но только второй из трех примеров является авто-свойством.
11

маленький полный образец:

using System.ComponentModel;

private bool bShowGroup ;
[Description("Show the group table"), Category("Sea"),DefaultValue(true)]
public bool ShowGroup
{
    get { return bShowGroup; }
    set { bShowGroup = value; }
}
  • 32
    Это не сработает. DefaultValueAttribute - просто подсказка сериализации, он не установит для ShowGroup значение true поскольку для любого логического значения по умолчанию установлено значение false .
10

В дополнение к уже принятому ответу, для сценария, когда вы хотите определить свойство по умолчанию как функцию от других свойств, вы можете использовать нотацию тела выражения на С# 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

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
  • 1
    Как указано выше, использование отражения для инициализации значений по умолчанию является медленным и избыточным. Инициализируйте в конструкторе, используйте неавтоматическое свойство или на c # 6 и выше, используйте упрощенную запись, показанную в принятом ответе
7

В С# 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

  • 1
    Это не верно; нет проблемы "двойного назначения" с использованием конструктора ....
  • 0
    Вау, это взрыв из прошлого. Кажется, я вспоминаю, что это было основано на чтении спецификации (частичная выдержка здесь: msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx ). Учитывая время и количество версий (и Roslyn), это уже не может иметь место. Хотя встречная ссылка была бы оценена.
Показать ещё 1 комментарий
4

В конструкторе. Цель конструктора - инициализировать его членами данных.

2
private string name;
public string Name 
{
    get 
    {
        if(name == null)
        {
            name = "Default Name";
        }
        return name;
    }
    set
    {
        name = value;
    }
}
  • 0
    Я думаю, что спрашивающий хотел авто-свойство, то есть неабстрактное свойство в классе или структуре, которые вы используете просто get; с точкой с запятой (часто в сочетании с set; ), чтобы указать, что компилятор должен автоматически генерировать тело метода доступа get .
  • 0
    Кроме того, этот код имеет побочный эффект возврата к значению по умолчанию, когда явно установлено значение NULL.
2
public Class ClassName{
    public int PropName{get;set;}
    public ClassName{
        PropName=0;  //Default Value
    }
}
2

Вы пытались использовать DefaultValueAttribute или ShouldSerialize и Reset в сочетании с конструктором? Я чувствую, что один из этих двух методов необходим, если вы создаете класс, который может отображаться на поверхности конструктора или в сетке свойств.

  • 3
    Нет, те работают только в дизайнере, а не в «реальной жизни».
  • 2
    @Hans: они работают «в реальной жизни», но только для установки значения по умолчанию (например, это влияет на сериализацию xml). Здесь требуется не «значение по умолчанию», а «начальное значение».
1

Используйте конструктор, потому что "Когда конструктор закончен, конструкция должна быть закончена". свойства похожи на состояния, которые хранятся ваши классы, если вам нужно было инициализировать состояние по умолчанию, вы сделали бы это в своем конструкторе.

1

Чтобы уточнить, да, вам нужно установить значения по умолчанию в конструкторе для объектов класса. Вам нужно будет убедиться, что конструктор существует с соответствующим модификатором доступа для построения, где он используется. Если объект не создается, например. он не имеет конструктора (например, статические методы), тогда значение по умолчанию может быть задано полем. Примером здесь является то, что сам объект будет создан только один раз, и вы не создадите его.

@Даррен Копп - хороший ответ, чистый и правильный. И, повторяю, вы можете написать конструкторы для абстрактных методов. Вам просто нужно получить к ним доступ из базового класса при написании конструктора:

Конструктор в базовом классе:

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

Конструктор в Derived/Concrete/Sub-Class:

public SubClass() : base() { }

Дело здесь в том, что переменная экземпляра, взятая из базового класса, может похоронить имя вашего базового поля. Установка текущего значения объекта-объекта с помощью "this". позволит вам правильно сформировать ваш объект в отношении текущего экземпляра и требуемых уровней разрешений (модификаторы доступа), в которых вы его создаете.

1

Лично я не вижу смысла делать его собственностью вообще, если вы не собираетесь делать что-либо вообще за пределами авто-собственности. Просто оставьте это как поле. Преимущество инкапсуляции для этого элемента - просто красные сельди, потому что ничего не позади, чтобы инкапсулировать. Если вам когда-либо понадобится изменить базовую реализацию, вы по-прежнему можете реорганизовать их как свойства без нарушения какого-либо зависимого кода.

Хм... может быть, это будет вопрос его собственного вопроса позже

  • 2
    @ Joel : привязка данных и другие инструменты, основанные на отражении, часто ожидают свойства, а не поля.
  • 10
    Вы не можете преобразовать поле в свойство auto, не нарушая вызывающий код. Это может выглядеть одинаково, но сгенерированный код отличается. С автоматическими свойствами вызывающий код вызывает get_propname и set_propname за обложками, тогда как он просто обращается к полю напрямую, если это поле.
Показать ещё 3 комментария
0
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; }
}
  • 26
    Добро пожаловать в стек переполнения! Точно так же, как вы знаете, если вспомнить старый вопрос, подобный этому, обычно осуждают, если у вас нет хорошей новой информации. Однако в этом случае несколько других уже опубликовали информацию об атрибуте DefaultValue. Если кто-то уже опубликовал то, что вы собираетесь сказать, более целесообразно объявить его, нажав на стрелку вверх над номером рядом с его ответом.
  • 10
    @fire: комментирование требует 50 репутации. Голосование также требует репутации IIRC.
Показать ещё 2 комментария
-1

Я думаю, что это сделало бы это для ya givng SomeFlag по умолчанию false.

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

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

        SomeFlag = value;        
    }
}
  • 1
    Это не авто-собственность.
-2

Введите Prop и нажмите кнопку "TAB" и Visual Studio. Предложите вам следующий код,

public int MyProperty { get; set; }

после того, как вы сможете изменить модификатор, тип данных, имя и легко назначить набор и получить значения.

если вы должны использовать некоторую переменную в другом классе, вы можете использовать как follwing,

public static int MyProperty { get; set; }

в коде вы можете назначить переменную

MyProperty=1;

в другом классе вы можете использовать,

MessageBox.Show(Classname.MyProperty);
-6

инициализируется в строке, использование конструкторов для инициализации - это плохая практика и приведет к более резким изменениям позже.

  • 0
    -1 Вы имеете в виду объявление значения переменной в той же строке, что и ее определение? Поскольку мы говорим о свойствах здесь, это не относится.
  • 0
    Просто будь последовательным. -1 за отсутствие ответа.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню