Оба они генерируют ошибку, говоря, что они должны быть константой времени компиляции:
void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
Прежде всего, может ли кто-нибудь объяснить, почему эти значения не могут быть определены во время компиляции? И есть ли способ указать значение по умолчанию для необязательного объекта TimeSpan?
c#
c#-4.0
default-value
timespan
optional-parameters
Майк Патерас
источник
источник
new TimeSpan(2000)
виду, что это не означает 2000 миллисекунд, это означает 2000 «тактов», что составляет 0,2 миллисекунды или одну 10 000-ю из двух секунд.Ответы:
Вы можете легко обойти это, изменив свою подпись.
Я должен уточнить - причина, по которой эти выражения в вашем примере не являются константами времени компиляции, заключается в том, что во время компиляции компилятор не может просто выполнить TimeSpan.FromSeconds (2.0) и вставить байты результата в ваш скомпилированный код.
В качестве примера рассмотрим, пытались ли вы использовать DateTime.Now вместо этого. Значение DateTime.Now изменяется каждый раз при его выполнении. Или предположим, что TimeSpan.FromSeconds учитывает гравитацию. Это абсурдный пример, но правила констант времени компиляции не создают особых случаев только потому, что мы случайно знаем, что TimeSpan.FromSeconds детерминирован.
источник
<param>
, потому что оно не отображается в подписи.span = span ?? TimeSpan.FromSeconds(2.0);
с типом, допускающим значение NULL, в теле метода. Илиvar realSpan = span ?? TimeSpan.FromSeconds(2.0);
получить локальную переменную, не допускающую значения NULL.Мое наследие VB6 заставляет меня беспокоиться о том, что «нулевое значение» и «пропущенное значение» эквивалентны. В большинстве случаев это, вероятно, нормально, но у вас может быть непреднамеренный побочный эффект или вы можете проглотить исключительное условие (например, если источником
span
является свойство или переменная, которые не должны иметь значение null, но есть).Поэтому я бы перегрузил метод:
источник
Это отлично работает:
void Foo(TimeSpan span = default(TimeSpan))
источник
TimeSpan
значения, например, заданныеnew TimeSpan(2000)
,Набор значений, которые можно использовать в качестве значения по умолчанию, такой же, как и для аргумента атрибута. Причина в том, что значения по умолчанию кодируются в метаданные внутри
DefaultParameterValueAttribute
.Что касается того, почему это не может быть определено во время компиляции. Набор значений и выражений для таких значений, разрешенных во время компиляции, указан в официальной спецификации языка C # :
Тип
TimeSpan
не входит ни в один из этих списков и, следовательно, не может использоваться в качестве константы.источник
TimeSpan
может поместиться последний в этом спискеdefault(TimeSpan)
.при условии
default(TimeSpan)
, не является допустимым значением для функции.Или
предоставленное
new TimeSpan()
не является допустимым значением.Или
Это должно быть лучше, учитывая, что вероятность того, что
null
значение является допустимым значением для функции, редка.источник
TimeSpan
является особым случаем дляDefaultValueAttribute
и указывается с использованием любой строки, которая может быть проанализирована с помощьюTimeSpan.Parse
метода.источник
Мое предложение:
Кстати
TimeSpan.FromSeconds(2.0)
, не равноnew TimeSpan(2000)
- тики принимает конструктор.источник
Другие ответы дали отличные объяснения того, почему необязательный параметр не может быть динамическим выражением. Но, повторюсь, параметры по умолчанию ведут себя как константы времени компиляции. Это означает, что компилятор должен уметь их оценивать и давать ответ. Есть люди, которые хотят, чтобы в C # была добавлена поддержка компилятора, оценивающего динамические выражения при обнаружении константных объявлений - такого рода функция была бы связана с маркировкой методов «чистыми», но это не реальность прямо сейчас и, возможно, никогда не будет.
Альтернативой использованию параметра C # по умолчанию для такого метода было бы использование шаблона, представленного
XmlReaderSettings
. В этом шаблоне определите класс с конструктором без параметров и общедоступными свойствами. Затем замените все параметры на значения по умолчанию в вашем методе с объектом этого типа. Даже сделайте этот объект необязательным, указав для него значение по умолчаниюnull
. Например:Чтобы вызвать, используйте этот странный синтаксис для создания и назначения свойств в одном выражении:
Downsides
Это действительно тяжелый подход к решению этой проблемы. Если вы пишете быстрый и грязный внутренний интерфейс и делаете
TimeSpan
nullable и обрабатываете null, как желаемое значение по умолчанию, все будет работать нормально, сделайте это вместо этого.Кроме того, если у вас есть большое количество параметров или вы вызываете метод в тесном цикле, это приведет к накладным расходам на создание экземпляров класса. Конечно, при вызове такого метода в замкнутом цикле было бы естественно и даже очень легко повторно использовать экземпляр
FooSettings
объекта.Преимущества
Как я упоминал в комментарии к примеру, я думаю, что этот шаблон отлично подходит для общедоступных API. Добавление новых свойств в класс - это неразрывное изменение ABI, поэтому вы можете добавлять новые необязательные параметры, не изменяя сигнатуру вашего метода, используя этот шаблон, предоставляя недавно скомпилированному коду дополнительные параметры, продолжая поддерживать старый скомпилированный код без дополнительной работы ,
Кроме того, поскольку встроенные в C # параметры метода по умолчанию обрабатываются как константы времени компиляции и запекаются в callite, параметры по умолчанию будут использоваться кодом только после его перекомпиляции. Создавая экземпляр объекта настроек, вызывающий объект динамически загружает значения по умолчанию при вызове вашего метода. Это означает, что вы можете обновить значения по умолчанию, просто изменив свой класс настроек. Таким образом, этот шаблон позволяет вам изменять значения по умолчанию без необходимости перекомпилировать вызывающие объекты, чтобы увидеть новые значения, если это необходимо.
источник