Как дать автоматическому свойству 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;
}
}
Есть ли способ лучше?





Вы пробовали использовать DefaultValueAttribute или Методы ShouldSerialize и Reset вместе с конструктором? Мне кажется, что один из этих двух методов необходим, если вы создаете класс, который может отображаться на поверхности конструктора или в сетке свойств.
В C# 5 и более ранних версиях, чтобы присвоить автоматически реализуемым свойствам начальное значение, вы должны сделать это в конструкторе.
Начиная с C# 6.0, вы можете указать начальное значение в строке. Синтаксис:
public int X { get; set; } = x; // C# 6 or higher
DefaultValueAttribute предназначен для использования разработчиком VS (или любым другим потребителем) для указания значения по умолчанию, а не начального значения. (Даже если в спроектированном объекте начальное значение является значением по умолчанию).
Во время компиляции DefaultValueAttribute не повлияет на сгенерированный IL и не будет считан для инициализации свойства этим значением (см. Атрибут DefaultValue не работает с моим автоматическим свойством).
Примером атрибутов, влияющих на IL, являются ThreadStaticAttribute, CallerMemberNameAttribute, ...
Иногда я использую это, если не хочу, чтобы он действительно устанавливался и сохранялся в моей базе данных:
class Person
{
private string _name;
public string Name
{
get
{
return string.IsNullOrEmpty(_name) ? "Default Name" : _name;
}
set { _name = value; }
}
}
Очевидно, что если это не строка, я мог бы сделать объект допускающим значение NULL (double ?, int?) И проверить, является ли он нулевым, вернуть значение по умолчанию или вернуть значение, для которого он установлен.
Затем я могу проверить свой репозиторий, чтобы увидеть, является ли он моим по умолчанию и не сохраняется, или выполнить проверку бэкдора, чтобы увидеть истинный статус резервного значения, перед сохранением.
Надеюсь, это поможет!
@abatishchev: хотя это не одно и то же. Код тиглей вернет «Имя по умолчанию», если строка «» или пустая, но при использовании вашего подхода вернет «Имя по умолчанию» только в том случае, если оно равно нулю. Кроме того, обсуждается ли "??" или "IsNullOrEmpty" более понятно.
Точка является значением по умолчанию, поэтому проверка, допускающая значение NULL, отменяет точку. Ответ Кита демонстрирует это, инициализировав его в Ctor. Если это для дБ, я действительно не вижу большой разницы, чем наличие значения столбца по умолчанию и сделать его ненулевым столбцом, который будет более эффективным независимо от количества полей класса. Я не буду голосовать против, но призываю разработчиков подумать об этом, а не делать пустые / пустые проверки в ваших процедурах собственности.
Чтобы прояснить каждый раз, когда вы вызываете свойство класса, он будет выполнять нулевую / пустую проверку, тогда как дБ будет делать это только при INSERT или UPDATE, которые обычно составляют 20% работы дБ. Вместо этого потенциально каждое строковое свойство имеет дополнительный вызов, это пустая трата циклов процессора и плохой выбор дизайна IMHO. Кроме того, теперь есть Null Ref Type, поэтому работа с nullable более распространена.
Лично я не вижу смысла вообще делать это свойством, если вы вообще не собираетесь делать что-либо, кроме свойства auto. Просто оставьте это как поле. Преимущество инкапсуляции для этих элементов - просто отвлекающий маневр, потому что за ними нечего инкапсулировать. Если вам когда-либо понадобится изменить базовую реализацию, вы по-прежнему можете реорганизовать их как свойства, не нарушая какой-либо зависимый код.
Хм ... может быть, это будет предметом отдельного вопроса позже
Вы не можете преобразовать поле в свойство auto, не нарушив вызывающий код. Он может выглядеть одинаково, но сгенерированный код отличается. С автоматическими свойствами вызывающий код вызывает get_propname и set_propname за крышками, тогда как он просто обращается к полю напрямую, если это поле.
Да, это очень давно - с тех пор я пересмотрел свою позицию: stackoverflow.com/questions/205568/…
Вы также не можете получить доступ к полю через границы домена приложения - только к свойству или методу.
@ Джоэл: привязка данных и другие инструменты на основе отражения часто ожидают свойства, а не поля.
И вы не можете объявить поле в интерфейсе только как свойство
Если ваша позиция изменилась, возможно, вам стоит отредактировать / удалить этот ответ.
Когда вы встраиваете начальное значение для переменной, это в любом случае будет сделано неявно в конструкторе.
Я бы сказал, что этот синтаксис был лучшей практикой в 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";
В 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
Это не правильно; нет проблемы "двойного присваивания" с / с использованием конструктора ....
Вау, это взрыв из прошлого. Я, кажется, припоминаю, что это было основано на чтении спецификации (частичный отрывок здесь: msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx). Учитывая время и количество версий (и Roslyn), этого больше не могло быть. Хотя обратная ссылка будет оценена.
Назначение по умолчанию происходит автоматически, независимо от того, используете ли вы начальное значение или присваиваете в конструкторе. Существует небольшая семантическая разница - присвоение полей происходит до вызова конструктора, но присвоение null все равно произойдет. См. 10.4.5 "все поля экземпляра ... сначала инициализируются значениями по умолчанию, а затем выполняются инициализаторы поля экземпляра" msdn.microsoft.com/en-us/library/aa645757(VS.71).aspx
небольшой полный образец:
using System.ComponentModel;
private bool bShowGroup ;
[Description("Show the group table"), Category("Sea"),DefaultValue(true)]
public bool ShowGroup
{
get { return bShowGroup; }
set { bShowGroup = value; }
}
Это не сработает. DefaultValueAttribute - это просто подсказка сериализации, он не установит ShowGroup в true, потому что значение по умолчанию для любого логического значения - false.
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; }
}
Добро пожаловать в Stack Overflow! Просто чтобы вы знали, поднимать старый вопрос, как этот, обычно не одобряют, если у вас нет хорошей новой информации. Однако в этом случае несколько других уже опубликовали информацию об атрибуте DefaultValue. Если кто-то уже опубликовал то, что вы собирались сказать, более целесообразно проголосовать за него, щелкнув стрелку вверх над числом рядом с его ответом.
@fire: Комментирование требует 50 репутации. Голосование также требует репутации, IIRC.
-1, так как это не инициализирует свойство значением по умолчанию.
работает только с дизайнером, как указано во всех предыдущих ответах
Отредактировано 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; }
}
Чтобы уточнить, да, вам нужно установить значения по умолчанию в конструкторе для объектов, производных от класса. Вам нужно будет убедиться, что конструктор существует с правильным модификатором доступа для строительства, если он используется. Если объект не создан, например у него нет конструктора (например, статических методов), тогда значение по умолчанию может быть установлено полем. Причина здесь в том, что сам объект будет создан только один раз, и вы не создаете его экземпляр.
@ Даррен Копп - хороший ответ, чистый и правильный. И, повторюсь, вы МОЖЕТЕ писать конструкторы для абстрактных методов. Вам просто нужно получить к ним доступ из базового класса при написании конструктора:
Конструктор в базовом классе:
public BaseClassAbstract()
{
this.PropertyName = "Default Name";
}
Конструктор в производном / конкретном / подклассе:
public SubClass() : base() { }
Дело в том, что переменная экземпляра, полученная из базового класса, может похоронить ваше имя базового поля. Установка текущего значения экземпляра объекта с помощью this. позволит вам правильно сформировать ваш объект относительно текущего экземпляра и требуемых уровней разрешений (модификаторов доступа), на которых вы его создаете.
Мое решение - использовать настраиваемый атрибут, который обеспечивает инициализацию свойства значения по умолчанию с помощью константы или с помощью инициализатора типа свойства.
[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
Как указано выше, использование отражения для инициализации значений по умолчанию является медленным и излишним. Инициализируйте конструктор, используйте свойство, отличное от auto, или в C# 6 и выше используйте упрощенную нотацию, показанную в принятом ответе.
Я думаю, что это сделало бы это для того, чтобы дать SomeFlag значение по умолчанию false.
private bool _SomeFlagSet = false;
public bool SomeFlag
{
get
{
if (!_SomeFlagSet)
SomeFlag = false;
return SomeFlag;
}
set
{
if (!_SomeFlagSet)
_SomeFlagSet = true;
SomeFlag = value;
}
}
Это не автоматическая собственность.
Начиная с C# 6.0, мы можем присвоить значение по умолчанию автоматически реализуемым свойствам.
public string Name { get; set; } = "Some Name";
Мы также можем создать автоматически реализуемое свойство только для чтения, например:
public string Name { get; } = "Some Name";
См .: C# 6: Первые реакции, инициализаторы для автоматически реализуемых свойств - Автор: Джон Скит
public Class ClassName{
public int PropName{get;set;}
public ClassName{
PropName=0; //Default Value
}
}
Используйте конструктор, потому что «Когда конструктор закончен, строительство должно быть завершено». Свойства подобны состояниям, которые хранятся в ваших классах, если бы вам нужно было инициализировать состояние по умолчанию, вы бы сделали это в своем конструкторе.
В конструкторе. Конструктор предназначен для инициализации членов данных.
Вы можете сделать это в самом объявлении 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;
}
}
У меня еще нет C# 6.0, и я проверял, какая версия мне нужна для значений по умолчанию в автоматических свойствах. Устраняет ли C# 6.0 необходимость наличия { get; set; } или { get; private set; }, иначе установка значения будет заблокирована компилятором?
В дополнение к уже принятому ответу для сценария, когда вы хотите определить свойство по умолчанию как функция других свойств, вы можете использовать обозначение тела выражения в 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.
Подробности о MSDN
В версии 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;
}
Это нормально, но только второй из трех примеров является автоматическим свойством.
Также рассмотрим пример class C { public DateTime P { get; } = DateTime.Now; public DateTime Q => DateTime.Now; }, где оба свойства P и Q имеют только геттер, но поведение P и Q сильно различается!
private string name;
public string Name
{
get
{
if (name == null)
{
name = "Default Name";
}
return name;
}
set
{
name = value;
}
}
Я думаю, что задающий вопрос хотел автоматическое свойство, то есть неабстрактное свойство в классе или структуре, где вы используете только get; с точкой с запятой (часто в сочетании с set;), чтобы указать, что компилятор должен автоматически сгенерировать тело средства доступа get.
Кроме того, этот код имеет побочный эффект возврата к значению по умолчанию, если явно задано значение null.
Вы можете просто написать вот так
public sealed class Employee
{
public int Id { get; set; } = 101;
}
Я знаю, что это старый вопрос, но он возник, когда я искал, как получить значение по умолчанию, которое наследуется с возможностью переопределения, я придумал
//base class
public class Car
{
public virtual string FuelUnits
{
get { return "gasoline in gallons"; }
protected set { }
}
}
//derived
public class Tesla : Car
{
public override string FuelUnits => "ampere hour";
}
Внимание: это не устанавливает начальное значение, а создает метод доступа get, который возвращает постоянную строку. Если значение FuelUnits установлено на другую строку, FuelUnits проигнорирует это значение и продолжит возвращать литеральную строку, определенную в get.
return _name ?? "Default Name";наверное даже понятнее, чем ваш