C#4.0:我可以使用TimeSpan作为带有默认值的可选参数吗?

Mik*_*ras 118 c# timespan default-value optional-parameters c#-4.0

这两个都会产生错误,说它们必须是编译时常量:

void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
Run Code Online (Sandbox Code Playgroud)

首先,有人可以解释为什么在编译时无法确定这些值吗?有没有办法为可选的TimeSpan对象指定默认值?

Jos*_*osh 165

您可以通过更改签名轻松解决此问题.

void Foo(TimeSpan? span = null) {

   if (span == null) { span = TimeSpan.FromSeconds(2); }

   ...

}
Run Code Online (Sandbox Code Playgroud)

我应该详细说明 - 您的示例中的表达式不是编译时常量的原因是因为在编译时,编译器不能简单地执行TimeSpan.FromSeconds(2.0)并将结果的字节粘贴到已编译的代码中.

例如,考虑您是否尝试使用DateTime.Now.DateTime.Now的值每次执行时都会更改.或者假设TimeSpan.FromSeconds考虑了重力.这是一个荒谬的例子,但编译时常量的规则并不是因为我们碰巧知道TimeSpan.FromSeconds是确定性的而造成特殊情况.

  • 也可以用`span = span ?? TimeSpan.FromSeconds(2.0);`具有可空类型,在方法体中.或者`var realSpan = span ?? TimeSpan.FromSeconds(2.0);`获取一个不可为空的局部变量. (17认同)
  • 现在记录`<param>`中的默认值,因为它在签名中不可见. (13认同)
  • 我不喜欢这样的事情是它向函数的用户暗示这个函数"工作"的空间跨度.但那不是真的!就函数的实际逻辑而言,Null是*not*有效的span值.我希望有一种更好的方式,看起来不像代码味道...... (5认同)
  • @MattHickford - 然后你必须提供一个重载方法或者以毫秒为参数. (4认同)
  • 我不能这样做,我使用特殊值null为其他东西. (3认同)
  • @JoeCool这并不是那么不寻常.框架中的许多方法接受IFormatProvider,并且如果传递null,则使用默认实现. (2认同)

pho*_*oog 29

我的VB6遗产让我感到不安,认为"空值"和"缺失值"是等价的.在大多数情况下,它可能很好,但是您可能会产生意想不到的副作用,或者您可能会吞下异常条件(例如,如果源span是属性或变量,不应该为null,但是).

因此我会重载该方法:

void Foo()
{
    Foo(TimeSpan.FromSeconds(2.0));
}
void Foo(TimeSpan span)
{
    //...
}
Run Code Online (Sandbox Code Playgroud)

  • +1 为这项伟大的技术。默认参数应该只与 const 类型一起使用,真的。否则不靠谱。 (3认同)
  • 这是默认值替换的'时间'方法,对于这种情况,我认为这是最难看的答案;)就其自身而言,它不一定对接口有效,因为你真的想要默认值一个地方.在这种情况下,我发现扩展方法是一个有用的工具:接口有一个包含所有参数的方法,然后在接口旁边的静态类中声明的一系列扩展方法实现了各种重载的默认值. (2认同)

小智 21

这很好用:

void Foo(TimeSpan span = default(TimeSpan))

  • 欢迎来到Stack Overflow.你的答案似乎是你*可以*提供一个默认参数值,只要它是编译器允许的一个非常具体的值.我明白了吗?(你可以[编辑]你的答案澄清.)如果它展示了如何利用编译器允许获得最初寻求的问题,这将是一个更好的答案,这是任意*其他*`TimeSpan`值,例如`new TimeSpan(2000)`给出的值. (4认同)
  • 使用某些特定默认值的替代方法是使用私有静态只读 TimeSpan defaultTimespan = Timespan.FromSeconds(2) 结合默认构造函数和采用时间跨度的构造函数。public Foo() : this(defaultTimespan) 和 public Foo(Timespan ts) (2认同)

Jar*_*Par 15

可用作默认值的值集与可用于属性参数的值相同.原因是默认值被编码到内部的元数据中DefaultParameterValueAttribute.

至于为何在编译时无法确定.在编译时允许的值和表达式集合在官方C#语言规范中列出:

C#6.0 - 属性参数类型:

属性类的位置和命名参数的类型仅限于属性参数类型,它们是:

  • 其中以下类型:bool,byte,char,double,float,int,long,sbyte,short,string,uint,ulong,ushort.
  • 类型object.
  • 类型System.Type.
  • 枚举类型.
    (如果它具有公共可访问性,并且它嵌套的类型(如果有的话)也具有公共可访问性)
  • 上述类型的一维阵列.

该类型TimeSpan不适合任何这些列表,因此不能用作常量.

  • 轻微的挑选:调用静态方法不适合任何列表.`TimeSpan`可以适合这个列表中的最后一个`default(TimeSpan)`有效. (2认同)

naw*_*fal 12

void Foo(TimeSpan span = default(TimeSpan))
{
    if (span == default(TimeSpan)) 
        span = TimeSpan.FromSeconds(2); 
}
Run Code Online (Sandbox Code Playgroud)

提供default(TimeSpan)的不是函数的有效值.

要么

//this works only for value types which TimeSpan is
void Foo(TimeSpan span = new TimeSpan())
{
    if (span == new TimeSpan()) 
        span = TimeSpan.FromSeconds(2); 
}
Run Code Online (Sandbox Code Playgroud)

提供new TimeSpan()的不是有效值.

要么

void Foo(TimeSpan? span = null)
{
    if (span == null) 
        span = TimeSpan.FromSeconds(2); 
}
Run Code Online (Sandbox Code Playgroud)

考虑到null价值是函数的有效值的机会很少,这应该更好.


小智 5

TimeSpan是 的特例,DefaultValueAttribute并且使用可以通过该TimeSpan.Parse方法解析的任何字符串指定。

[DefaultValue("0:10:0")]
public TimeSpan Duration { get; set; }
Run Code Online (Sandbox Code Playgroud)