Другие ответы дали большие объяснения того, почему необязательный параметр не может быть динамическим выражением. Но, чтобы пересчитать, параметры по умолчанию ведут себя как константы времени компиляции. Это означает, что компилятор должен уметь оценивать их и предлагать ответ. Есть люди, которые хотят, чтобы C # добавлял поддержку компилятора, оценивающего динамические выражения, при обнаружении константных объявлений - такая функция была бы связана с маркировкой методов «pure», но сейчас это не является реальностью и никогда не может быть *. 1003 *
Одной альтернативой использованию параметра C # по умолчанию для такого метода будет использование шаблона, примером которого является XmlReaderSettings
. В этом шаблоне определите класс с конструктором без параметров и общедоступными свойствами. Затем замените все параметры значениями по умолчанию в вашем методе на объект этого типа. Даже сделать этот объект необязательным, указав для него значение по умолчанию null
. Например:
public class FooSettings
{
public TimeSpan Span { get; set; } = TimeSpan.FromSeconds(2);
// I imagine that if you had a heavyweight default
// thing you’d want to avoid instantiating it right away
// because the caller might override that parameter. So, be
// lazy! (Or just directly store a factory lambda with Func<IThing>).
Lazy<IThing> thing = new Lazy<IThing>(() => new FatThing());
public IThing Thing
{
get { return thing.Value; }
set { thing = new Lazy<IThing>(() => value); }
}
// Another cool thing about this pattern is that you can
// add additional optional parameters in the future without
// even breaking ABI.
//bool FutureThing { get; set; } = true;
// You can even run very complicated code to populate properties
// if you cannot use a property initialization expression.
//public FooSettings() { }
}
public class Bar
{
public void Foo(FooSettings settings = null)
{
// Allow the caller to use *all* the defaults easily.
settings = settings ?? new FooSettings();
Console.WriteLine(settings.Span);
}
}
Для вызова используйте этот странный синтаксис для создания и присваивания свойств всем в одном выражении:
bar.Foo(); // 00:00:02
bar.Foo(new FooSettings { Span = TimeSpan.FromDays(1), }); // 1.00:00:00
bar.Foo(new FooSettings { Thing = new MyCustomThing(), }); // 00:00:02
Downsides
Это действительно тяжеловесный подход к решению этой проблемы. Если вы пишете быстрый и грязный внутренний интерфейс и делает обнуляемый TimeSpan
и рассматриваете ноль как желаемое значение по умолчанию , то будет работать нормально.
Кроме того, если у вас есть большое количество параметров или вы вызываете метод в узком цикле, это приведет к дополнительным затратам на создание экземпляров классов. Конечно, при вызове такого метода в узком цикле может быть естественным и даже очень легко повторно использовать экземпляр объекта FooSettings
.
Преимущества
Как я уже упоминал в комментарии к примеру, я думаю, что этот шаблон отлично подходит для публичных API. Добавление новых свойств в класс - это непрерывное изменение ABI, поэтому вы можете добавлять новые необязательные параметры без изменения сигнатуры вашего метода, используя этот шаблон - предоставляя более недавно скомпилированному коду больше возможностей, продолжая при этом поддерживать старый скомпилированный код без дополнительной работы .
Кроме того, поскольку встроенные в C # параметры метода по умолчанию обрабатываются как константы времени компиляции и запекаются в месте вызова, параметры по умолчанию будут использоваться кодом только после его перекомпиляции. Создавая объект настроек, вызывающая сторона динамически загружает значения по умолчанию при вызове вашего метода. Это означает, что вы можете обновить настройки по умолчанию, просто изменив свой класс настроек. Таким образом, этот шаблон позволяет изменять значения по умолчанию без необходимости перекомпиляции вызывающих абонентов для просмотра новых значений, если это необходимо.