Как я могу по умолчанию параметр для Guid.Empty в C #?

178

Я хочу сказать:

public void Problem(Guid optional = Guid.Empty)
{
}

Но компилятор жалуется, что Guid.Empty не является постоянной времени компиляции.

Поскольку я не хочу менять API, я не могу использовать:

 Nullable<Guid>
Ян Рингроз
источник
Что не так с переключением на Nullable<Guid> optional = null(или, более кратко, Guid? optional = null)? Любые Руководства, переданные ему в настоящее время, будут принудительно выполняться без каких-либо изменений кода.
НХ.

Ответы:

235

Решение

Вы можете использовать new Guid()вместо

public void Problem(Guid optional = new Guid())
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Вы также можете использовать default(Guid)

default(Guid)и будет работать точно так же new Guid().

Поскольку Guid является типом значения, а не ссылочным типом, то default(Guid)есть не равен, nullнапример, вместо этого он равен вызову конструктора по умолчанию.

Что означает, что это:

public void Problem(Guid optional = default(Guid))
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Это точно так же, как оригинальный пример.

объяснение

Почему не Guid.Emptyработает?

Причина, по которой вы получаете ошибку, заключается в том, что Emptyона определяется как:

public static readonly Guid Empty;

Таким образом, это является переменной, не является постоянным (определяется как static readonlyне так const). Компилятор может иметь только значения, известные компилятору, в качестве значений параметров метода по умолчанию (не известно только во время выполнения).

Основная причина в том, что вы не можете иметь constничего struct, в отличие от, enumнапример. Если вы попробуете это, он не скомпилируется.

Причина еще раз в том, что structэто не примитивный тип.
Список всех примитивных типов в .NET см. По адресу http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(обратите внимание, что enumобычно наследуется int, что является примитивом)

Но new Guid()тоже не константа!

Я не говорю, что нужна постоянная. Нужно что-то, что можно решить во время компиляции. Emptyэто поле, поэтому его значение неизвестно во время компиляции (только в самом начале времени выполнения).

Значение параметра по умолчанию должно быть известно во время компиляции, которое может быть constзначением или определенным с помощью функции C #, которая делает значение известным во время компиляции, например default(Guid)или new Guid()(которое определяется во время компиляции для structs, поскольку вы не можете изменить structконструктор в код).

Хотя вы можете предоставить defaultили newлегко, вы не можете предоставить const(потому что это не примитивный тип или enumкак описано выше). Итак, опять же, не говоря о том, что самому необязательному параметру нужна постоянная, но известная компилятору величина.

Meligy
источник
5
Ну, для этого не нужно нормальное постоянное выражение - например, new Guid()это не постоянное выражение. Спецификация C # довольно четко определяет, что разрешено, включая, но не ограничиваясь, константы (Просто чтобы прояснить, это эффективно является константой времени компиляции, а не «константным выражением» в терминах спецификации C #).
Джон Скит
3
Прочитайте объяснение части в моем ответе. Добавлено для ответа на эту часть.
Meligy
Хорошее объяснение. Спасибо!
Дэрил
Вы можете использовать только в defaultнастоящее время :)
Джошит
151

Guid.Emptyэквивалентно new Guid(), что эквивалентно default(Guid). Таким образом, вы можете использовать:

public void Problem(Guid optional = default(Guid))

или

public void Problem(Guid optional = new Guid())

Обратите внимание, что new Foo()значение применимо только когда:

  • Вы действительно вызываете конструктор без параметров
  • Foo это тип значения

Другими словами, когда компилятор знает, что это действительно просто значение по умолчанию для типа :)

(Интересно, что я на 99,9% уверен, что он не вызовет никакого new Foo()созданного вами пользовательского конструктора. Вы не можете создать такой конструктор в типе значения в C #, но вы можете сделать это в IL.)

Вы можете использовать эту default(Foo)опцию для любого типа.

Джон Скит
источник
Теперь, почему сообщение об ошибке компилятора не говорит мне об этом, компилятор может проверить случай Guid.Empty и выдать более полезное сообщение.
Ян Рингроз
4
@Ian Ringrose: если честно, я не думаю, что у компилятора должны быть вообще сообщения о типах.
Джон Скит
2
Установка параметра по умолчанию для нового объекта создает новый объект каждый раз, когда метод вызывается в PHP; но создает только один объект для всей программы на Python. На самом деле, я считаю это одним из очень немногих недостатков дизайна Python . Я несколько рад, что C # (и VB.Net) избежали этой проблемы, просто запретив новые объекты в параметрах по умолчанию ... хотя бывают случаи, когда эта возможность действительно хороша в PHP.
BlueRaja - Дэнни Пфлюгофт
1
что может быть причиной того, что эта же вещь не будет работать в действии контроллера ASP.NET MVC? Все другие необязательные параметры работают (int, string), но не работают для GUID, говорит «Ошибка сервера в« / »приложении. Словарь параметров содержит пустую запись для параметра« categoryId »ненулевого типа« System.Guid ». '.. "Код прекрасно компилируется с обеими спецификациями (по умолчанию (Guid) и с новым Guid ()), но выдает эту ошибку.
Маре
@mare: я не знаю, я боюсь. Другой вариант - использовать Nullable<Guid>потенциально.
Джон Скит
18

Вы не можете использовать:

default ( Guid ) ?

Ник
источник
1
Нет Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
рекурсивный
4
Извините, я не имел в виду ?? как оператор, но как подчеркнутый знак вопроса - я буду редактировать!
Ник
9

Принятый ответ не работает в ASP.NET MVC и вызывает эту ошибку во время выполнения:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'optional' of non-nullable type 'System.Guid' for method 'System.Web.Mvc.ActionResult Problem(System.Guid)' ....

Вместо этого вы можете сделать следующее:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}
Majix
источник
Я вижу причину
отрицательного
Если вы не хотите явно назначить параметр «необязательный» с пустым значением Guid, я думаю, что это наиболее естественный способ определения необязательного параметра типа Guid.
Гонсало Мендес
4

Компилятор вполне корректен; Guid.Emptyне является константой времени компиляции. Вы можете попробовать перегрузить метод следующим образом:

public void Problem()
{
    Problem(Guid.Empty);
}
CVn
источник
Я сказал, что не хочу менять API, метод, который я пытаюсь приручить, имеет более 10 пар!
Ян Рингроз
@Ian Ringrose, хотя я согласен с ним Guid x = default(Guid)в качестве решения, помните, что добавление другой перегрузки функции не усложняет API больше, чем добавление дополнительного аргумента. Это действительно то, что необязательный аргумент делает в любом случае.
десять четверг
@tenfour, должно быть много новых перегрузок функций, чтобы сделать то же самое, что 10 необязательных параметров!
Ян Рингроз
Если у вас есть общедоступная функция, для которой требуется более десяти параметров, возможно, использование одного необязательного аргумента на самом деле не является исправлением ... Кроме того, оглядываясь на исходный вопрос, вы действительно говорите, что не хотите «менять API "(и, как отмечает tenfour, разница между явной перегрузкой и необязательным аргументом на практике минимальна), но в вопросе о списке параметров нет такого рода монстра.
CVn
На самом деле, это идеальный ответ на проблему.
ДОК