В .NET тип значения (C # struct
) не может иметь конструктора без параметров. Согласно этому посту, это предусмотрено спецификацией CLI. Что происходит, так это то, что для каждого типа значения создается конструктор по умолчанию (компилятором?), Который инициализирует все члены нулем (или null
).
Почему запрещено определять такой конструктор по умолчанию?
Одно тривиальное использование для рациональных чисел:
public struct Rational {
private long numerator;
private long denominator;
public Rational(long num, long denom)
{ /* Todo: Find GCD etc. */ }
public Rational(long num)
{
numerator = num;
denominator = 1;
}
public Rational() // This is not allowed
{
numerator = 0;
denominator = 1;
}
}
Используя текущую версию C #, Rational по умолчанию - 0/0
это не так круто.
PS : параметры по умолчанию помогут решить эту проблему для C # 4.0 или будет вызван определяемый CLR конструктор по умолчанию?
Джон Скит ответил:
Чтобы использовать ваш пример, что бы вы хотели, чтобы произошло, когда кто-то сделал:
Rational[] fractions = new Rational[1000];
Должен ли он проходить через ваш конструктор 1000 раз?
Конечно, именно поэтому я и написал конструктор по умолчанию. CLR должен использовать конструктор обнуления по умолчанию, если явно не определен конструктор по умолчанию; Таким образом, вы платите только за то, что используете. Тогда, если мне нужен контейнер из 1000 нестандартных Rational
s (и я хочу оптимизировать 1000 конструкций), я буду использовать List<Rational>
вместо массива.
Эта причина, на мой взгляд, недостаточно сильна, чтобы предотвратить определение конструктора по умолчанию.
Rational()
вызывает ctor без параметров, а неRational(long num=0, long denom=1)
.new Rational()
будет вызывать конструктор, если он существует, однако, если он не существует,new Rational()
будет эквивалентноdefault(Rational)
. В любом случае вам рекомендуется использовать синтаксис,default(Rational)
когда вам нужно «нулевое значение» вашей структуры (что является «плохим» числом в предложенном вами дизайнеRational
). Значение по умолчанию для типа значенияT
всегдаdefault(T)
. Такnew Rational[1000]
что никогда не будет вызывать конструкторы структуры.denominator - 1
внутри структуры, так что значение по умолчанию становится 0/1Then if I want a container of 1000 non-default Rationals (and want to optimize away the 1000 constructions) I will use a List<Rational> rather than an array.
Почему вы ожидаете, что массив вызовет другой конструктор для List для структуры?Ответы:
Примечание: ответ ниже был написан задолго до C # 6, который планирует ввести возможность объявлять конструкторы без параметров в структурах - но они все равно не будут вызываться во всех ситуациях (например, для создания массива)(в конце эта функция не была добавлена в C # 6 ).РЕДАКТИРОВАТЬ: я отредактировал ответ ниже из-за проникновения Грауенвольфа в CLR.
CLR позволяет типам значений иметь конструкторы без параметров, а C # - нет. Я полагаю, что это потому, что это привело бы к ожиданию, что конструктор будет вызван, когда это не так. Например, рассмотрим это:
CLR может сделать это очень эффективно, просто выделив соответствующую память и обнулив все это. Если бы ему пришлось запускать конструктор MyStruct 1000 раз, это было бы намного менее эффективно. (На самом деле, это не делает - если вы делаете иметь конструктор без параметров, он не получает работать , когда вы создаете массив, или если у вас есть неинициализированный переменный экземпляр.)
Основное правило в C # - «значение по умолчанию для любого типа не может полагаться ни на какую инициализацию». Теперь они могли позволить определять конструкторы без параметров, но тогда не требовали, чтобы этот конструктор выполнялся во всех случаях - но это привело бы к еще большей путанице. (Или, по крайней мере, поэтому я считаю, что аргумент идет.)
РЕДАКТИРОВАТЬ: Чтобы использовать ваш пример, что бы вы хотели случиться, когда кто-то сделал:
Должен ли он проходить через ваш конструктор 1000 раз?
РЕДАКТИРОВАТЬ: (отвечая немного больше на вопрос) Конструктор без параметров не создается компилятором. Типы значений не должны иметь конструкторов, поскольку это касается CLR - хотя оказывается, что это возможно, если вы напишите это в IL. Когда вы пишете "
new Guid()
" в C #, он выделяет другой IL по сравнению с тем, что вы получаете, если вызываете обычный конструктор. Посмотрите этот ТАК вопрос немного больше по этому аспекту.Я подозреваю, что в структуре нет никаких типов значений с конструкторами без параметров. Несомненно, NDepend мог бы сказать мне, если бы я спросил это достаточно хорошо ... Тот факт, что C # запрещает это, является достаточно большим намеком, чтобы я подумал, что это, вероятно, плохая идея.
источник
Rational[] fractions = new Rational[1000];
также тратить нагрузку работы , еслиRational
это класс вместо структуры? Если так, почему у классов есть ctor по умолчанию?Rational
это класс, вы получите массив из 1000 нулевых ссылок.Структура является типом значения, и тип значения должен иметь значение по умолчанию, как только оно объявлено.
Если вы объявите два поля, как указано выше, без создания экземпляров, то остановите отладчик,
m
будет нулевым, ноm2
не будет. Учитывая это, конструктор без параметров не имеет смысла, фактически любой конструктор в структуре - это присваивать значения, сама вещь уже существует, просто объявив ее. Действительно, m2 вполне можно было бы использовать в приведенном выше примере и вызывать его методы, если они есть, а также манипулировать их полями и свойствами!источник
new
Это, а затем попытаться использовать ее членыХотя CLR это позволяет, C # не позволяет структурам иметь конструктор по умолчанию без параметров. Причина в том, что для типа значения компиляторы по умолчанию не генерируют конструктор по умолчанию и не генерируют вызов конструктора по умолчанию. Таким образом, даже если вы определили конструктор по умолчанию, он не будет вызван, и это вас только запутает.
Чтобы избежать таких проблем, компилятор C # запрещает определение конструктора по умолчанию пользователем. И поскольку он не генерирует конструктор по умолчанию, вы не можете инициализировать поля при их определении.
Или большая причина в том, что структура является типом значения, а типы значений инициализируются значением по умолчанию, а конструктор используется для инициализации.
Вам не нужно создавать экземпляр своей структуры с
new
ключевым словом. Вместо этого он работает как int; Вы можете напрямую получить к нему доступ.Структуры не могут содержать явных конструкторов без параметров. Члены структуры автоматически инициализируются к их значениям по умолчанию.
Конструктор по умолчанию (без параметров) для структуры может устанавливать значения, отличные от состояния с нулевым состоянием, что может быть неожиданным поведением. Поэтому среда выполнения .NET запрещает конструкторы по умолчанию для структуры.
источник
MyStruct s;
не вызывая предоставленный вами конструктор по умолчанию.Вы можете создать статическое свойство, которое инициализирует и возвращает «рациональное» число по умолчанию:
И используйте это как:
источник
Rational.Zero
может быть немного менее запутанным.Краткое объяснение:
В C ++ struct и class были просто двумя сторонами одной медали. Единственная реальная разница в том, что по умолчанию один был общедоступным, а другой - частным.
В .NET существует гораздо большая разница между структурой и классом. Главное, что struct предоставляет семантику типа-значения, а класс - семантику ссылочного типа. Когда вы начинаете думать о последствиях этого изменения, другие изменения начинают приобретать больше смысла, включая поведение конструктора, которое вы описываете.
источник
new
действительно должны быть написаны для вызова конструктора. В C ++ конструкторы вызываются скрытыми способами, при объявлении или создании экземпляров массивов. В C # либо все указатели, поэтому начинайте с нуля, либо это структура и должны начинаться с чего-то, но когда вы не можете написатьnew
... (например, массив init), это нарушит строгое правило C #.Я не видел эквивалента запоздалого решения, которое я собираюсь дать, так что вот оно.
используйте смещения для перемещения значений по умолчанию 0 в любое значение, которое вам нравится. здесь свойства должны использоваться вместо прямого доступа к полям. (возможно, с возможной функцией c # 7 вы лучше определите поля области свойств, чтобы они оставались защищенными от прямого доступа в коде.)
Это решение работает для простых структур только с типами значений (без типа ref или структуры, допускающей обнуление).
Это отличается от этого ответа, этот подход не является особенным регистром, а использует смещение, которое будет работать для всех диапазонов.
пример с перечислениями в качестве поля.
Как я уже сказал, этот прием может работать не во всех случаях, даже если структура имеет только поля значений, только вы знаете, работает ли он в вашем случае или нет. просто изучить. но вы получите общее представление.
источник
Просто особый случай. Если вы видите числитель 0 и знаменатель 0, сделайте вид, что он имеет значения, которые вы действительно хотите.
источник
Nullable<T>
(напримерint?
).new Rational(x,y)
где x и y окажутся 0?То, что я использую, является объединяющим нулем оператором (??), объединенным с полем поддержки как это:
Надеюсь это поможет ;)
Примечание. В настоящее время нулевое объединение назначается для C # 8.0.
источник
Вы не можете определить конструктор по умолчанию, потому что вы используете C #.
Структуры могут иметь конструкторы по умолчанию в .NET, хотя я не знаю ни одного конкретного языка, который бы его поддерживал.
источник
Вот мое решение дилеммы конструктора по умолчанию. Я знаю, что это запоздалое решение, но я думаю, что стоит отметить, что это решение.
игнорируя тот факт, что у меня есть статическая структура с именем null (Примечание: это только для всех положительных квадрантов), используя get; set; в C # вы можете использовать try / catch / finally для обработки ошибок, когда конкретный тип данных не инициализируется конструктором по умолчанию Point2D (). Я думаю, что это неуловимо, как решение некоторых людей на этот ответ. Именно поэтому я и добавляю свои. Использование функциональности get и set в C # позволит вам обойти этот конструктор по умолчанию без смысла и попытаться поймать то, что вы не инициализировали. Для меня это работает нормально, для кого-то еще вы можете добавить некоторые операторы if. Таким образом, в случае, если вы хотите установить Numerator / Denominator, этот код может помочь. Я просто хотел бы повторить, что это решение не выглядит красиво, возможно, работает еще хуже с точки зрения эффективности, но, для кого-то из более старой версии C # использование типов данных массива дает вам эту функциональность. Если вы просто хотите что-то, что работает, попробуйте это:
источник
источник