如何在C#中将参数默认为Guid.Empty?


178

我想说:

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

但是编译器抱怨Guid.Empty不是编译时间常数。

由于我不想更改API,因此无法使用:

 Nullable<Guid>


切换到Nullable<Guid> optional = null(或更简单地说,Guid? optional = null)有什么问题?当前传递给它的任何Guid都将强制执行而根本不需要任何代码更改。
NH。

Answers:


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 readonlynot const)。编译器只能将编译器已知的值用作方法参数的默认值(不是仅运行时已知的)。

根本原因是您不能拥有constany structenum例如。如果尝试,它将无法编译。

再次的原因是那struct不是原始类型。
有关.NET中所有原始类型的列表,请参见http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(请注意,enum通常是继承int,这是一个原始类型)

但是new Guid()不是一个常数呢!

我并不是说它需要一个常数。它需要可以在编译时决定的东西。Empty是一个字段,因此它的值在编译时未知(仅在运行时的最开始)。

必须在编译时知道默认参数值,该默认值可以是一个const值,也可以是使用C#功能定义的在编译时知道该 值的东西,例如default(Guid)new Guid()(这是在structs的编译时确定的,因为您不能在s中修改struct构造函数。码)。

尽管可以提供defaultnew很容易提供,但不能提供const(因为它不是原始类型或enum如上所述)。因此,同样,不是说可选参数本身需要一个常量,而是编译器已知的值。


5
好吧,它不需要正则表达式- new Guid()例如,不是常数表达式。C#规范非常清楚地定义了允许的内容,包括但不限于常量。(需要明确的是,它实际上一个编译时间常数,而不是C#规范术语中的“常数表达式”)。
乔恩·斯基特

3
阅读我的答复中的解释部分。添加来回答这一部分。
Meligy 2011年

很好的解释,谢谢!
达里尔

default现在只能使用:)
Joshit

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-Danny Pflughoeft 2011年

1
为何同一问题在ASP.NET MVC控制器操作中不起作用是什么原因?所有其他可选参数都可以工作(int,字符串),但不适用于GUID,请说“'/'应用程序中的服务器错误。参数字典包含非空类型'System.Guid的参数'categoryId'的空条目'..“该代码在两种规范(默认(Guid)和新的Guid())下都可以正常编译,但是会出现此错误。
2012年

@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();
    }
}

我看到了拒绝投票的原因:这个问题表明无法将API更改为使用Nullable <Guid>-足够公平
Majix 2014年

除非您要为参数“ optional”显式分配一个空的Guid值,否则我认为这是定义Guid类型的可选参数的最自然的方法。
贡萨洛·

4

编译器是正确的。Guid.Empty不是编译时常量。您可以尝试使方法重载如下:

public void Problem()
{
    Problem(Guid.Empty);
}

我说过我不想更改API,我要驯服的方法有10个参数!
伊恩·林格罗斯

@Ian Ringrose,尽管我同意Guid x = default(Guid)作为解决方案,但请记住,添加另一个函数重载不会使API变得比添加可选参数更复杂。无论如何,这确实是一个可选参数。
tenfour 2011年

@tenfour,它必须有许多新函数重载才能与10个可选parms相同!
伊恩·林罗斯

如果您有一个需要超过十个参数的公共函数,那么也许将单个参数设为可选并不是一个真正的解决方案。此外,回顾原始问题,您确实说过您不想“更改API”(就像Tenfour指出的那样,在实际中,显式重载和可选参数之间的差异很小),但在参数列表中却没有提到这种怪兽。
CVn

实际上,这是对问题的完美答案。
PKD
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.