С# - вызов конструктора struct, который имеет все дефолтные параметры
Сегодня я столкнулся с этой проблемой при создании struct
для хранения кучи данных. Вот пример:
public struct ExampleStruct
{
public int Value { get; private set; }
public ExampleStruct(int value = 1)
: this()
{
Value = value;
}
}
Выглядит отлично и денди. Проблема в том, что я пытаюсь использовать этот конструктор без указания значения и желая использовать значение по умолчанию 1 для параметра:
private static void Main(string[] args)
{
ExampleStruct example1 = new ExampleStruct();
Console.WriteLine(example1.Value);
}
Этот код выводит 0
и не выводит 1
. Причина в том, что все структуры имеют открытые конструкторы без параметров. Итак, как я называю this()
мой явный конструктор, в Main
то же самое происходит там, где new ExampleStruct()
на самом деле вызывает ExampleStruct()
, но не вызывает ExampleStruct(int value = 1)
. Поскольку он делает это, он использует int
значение по умолчанию 0 как Value
.
Что еще хуже, мой фактический код проверяет, что параметр int value = 1
находится в допустимом диапазоне внутри конструктора. Добавьте это в конструктор ExampleStruct(int value = 1)
выше:
if(value < 1 || value > 3)
{
throw new ArgumentException("Value is out of range");
}
Итак, поскольку он стоит прямо сейчас, конструктор по умолчанию фактически создал объект, который является недопустимым в контексте, в котором я нуждаюсь. Кто-нибудь знает, как я могу:
- A. Вызовите конструктор
ExampleStruct(int value = 1)
.
- В. Измените, как значения по умолчанию заполняются для конструктора
ExampleStruct()
.
- С. Некоторые другие варианты/варианты.
Кроме того, я знаю, что вместо своего свойства Value
я мог бы использовать такое поле:
public readonly int Value;
Но моя философия заключается в том, чтобы использовать поля конфиденциально, если они не являются const
или static
.
Наконец, причина, по которой я использую struct
вместо class
, состоит в том, что это просто объект для хранения неперемещаемых данных, должен быть полностью заполнен при его построении и когда передан как параметр, не должен быть null
(поскольку он передается значением как struct
), для которого предназначена структура.
Ответы
Ответ 1
Собственно, MSDN имеет хорошие рекомендации по struct
Рассмотрим определение структуры вместо класса, если экземпляры типа являются небольшими и обычно недолговечными или обычно внедряются в другие объекты.
Не определяйте структуру, если только тип имеет все следующие характеристики:
Он логически представляет одно значение, подобное примитивным типам (целое, двойное и т.д.).
У него размер экземпляра меньше 16 байт.
Это неизменное.
Его не нужно часто вставлять в бокс.
Обратите внимание, что они являются соображениями для рассмотрения a struct
, и его никогда не "это всегда должно быть структурой". Это связано с тем, что выбор использования struct
может иметь последствия для производительности и использования (как положительные, так и отрицательные) и должен быть выбран тщательно.
Обратите внимание, что они не рекомендуют struct
для вещей > 16 байт (тогда стоимость копирования становится дороже, чем копирование ссылки).
Теперь, для вашего случая, действительно нет хорошего способа сделать это иначе, чем создать factory для создания struct
для вас в состоянии по умолчанию или сделать что-то вроде trick
в вашей собственности чтобы обмануть его в инициализацию при первом использовании.
Помните, что struct
должен работать так, что new X()
== default(X)
, то есть вновь построенный struct
будет содержать значения по умолчанию для всех полей этого struct
. Это довольно очевидно, так как С# не позволит вам определить конструктор без параметров для struct
, хотя любопытно, что они разрешают все аргументы по умолчанию без предупреждения.
Таким образом, я бы предположил, что вы придерживаетесь class
и делаете его неизменным и просто проверяете null
на методы, к которым он передается.
public class ExampleClass
{
// have property expose the "default" if not yet set
public int Value { get; private set; }
// remove default, doesn't work
public ExampleStruct(int value)
{
Value = value;
}
}
Однако, если у вас обязательно есть struct
по другим причинам, но, пожалуйста, рассмотрите затраты struct
, такие как копирование и т.д., вы можете сделать это:
public struct ExampleStruct
{
private int? _value;
// have property expose the "default" if not yet set
public int Value
{
get { return _value ?? 1; }
}
// remove default, doesn't work
public ExampleStruct(int value)
: this()
{
_value = value;
}
}
Обратите внимание, что по умолчанию Nullable<int>
будет null
(т.е. HasValue == false
), поэтому, если это правда, мы еще не установили его и можем использовать оператор null-coalescing для возврата по умолчанию вместо 1
. Если мы установили его в конструкторе, он будет не нулевым и вместо этого примет это значение...
Ответ 2
Я не думаю, что хороший дизайн имеет struct
ExampleStruct
такой, что
default(ExampleStruct)
то есть. значение, где все поля экземпляра равны нулю /false/null, не действительное значение структуры. И, как вы знаете, когда вы говорите
new ExampleStruct()
что точно так же, как default(ExampleStruct)
и дает вам значение вашей структуры, где все поля (включая "сгенерированные" поля из авто-свойств) равны нулю.
Возможно, вы могли бы сделать это:
public struct ExampleStruct
{
readonly int valueMinusOne;
public int Value { get { return valueMinusOne + 1; } }
public ExampleStruct(int value)
{
valueMinusOne = value - 1;
}
}
Ответ 3
Я думаю, что компилятор фактически выбирает автоматический по умолчанию ctor для структур здесь http://msdn.microsoft.com/en-us/library/aa288208(v=vs.71).aspx, вместо того, чтобы использовать ваш ctor со значениями по умолчанию.
добавленные ссылки:
http://csharpindepth.com/Articles/General/Overloading.aspx (раздел "Дополнительные параметры" )
Ответ 4
Хотя некоторые языки (CIL, если не что иное) позволят определить конструктор структуры таким образом, что new T()
имеет поля, заданные по-разному, чем их "все нули" по умолчанию, С# и vb.net(и, возможно, большинство других языков ) считают, что поскольку элементы типа массива и поля классов всегда должны быть инициализированы до default(T)
, а поскольку их инициализация на то, что не соответствует new T()
, будет запутать, структуры не должны определять new T()
означает нечто иное, чем default(T)
.
Я бы предположил, что вместо того, чтобы пытаться сгладить параметр по умолчанию в параметризованном конструкторе, вы просто определяете статическое свойство struct, которое возвращает желаемое значение по умолчанию, и заменяет каждое вхождение new ExampleStruct()
на ExampleStruct.NiceDefault;
.
2015 Addendum
Похоже, что С# и VB.NET могут упростить запрет на определение конструкторов без параметров. Это может привести к утверждению типа Dim s = New StructType()
для назначения s
значения, отличного от значения, данного новым элементам массива типа StructType
. Я не очень увлекаюсь изменениями, учитывая, что new StructType
часто используется в тех местах, где что-то похожее на С# default(StructType)
было бы более подходящим, если бы оно существовало. VB.NET позволил бы Dim s As StructType = Nothing
, но это кажется довольно hokey.
Ответ 5
почему у вас есть public ExampleStruct(int value = 1) : this()
?
не должно быть public ExampleStruct(int value = 1)
? Я думаю, что :this()
создает конструктор без параметров.