未赋值变量的使用——变通

本文关键字:变通 赋值 变量 | 更新日期: 2023-09-27 18:01:18

我早就知道并习惯了c#中的这种行为,总的来说,我喜欢它。但有时编译器就是不够聪明。

我有一小段代码,现在我的变通方法不是一个大问题,但它可能在类似的情况下。

        bool gap=false;
        DateTime start; // = new DateTime();
        for (int i = 0; i < totaldays; i++)
        {
            if (gap)
            {
                if (list[i])
                {
                    var whgap = new WorkHistoryGap();
                    whgap.From = start; //unassigned variable error
                    whgap.To = dtFrom.AddDays(i);
                    return whgap;
                }
            }
            else
            {
                gap = true;
                start = dtFrom.AddDays(i);
            }
        }

我看到的问题是,如果你必须用一个没有默认构造函数的非空结构体来做这件事呢?如果start不是一个简单的DateTime对象,是否有办法解决这个问题?

未赋值变量的使用——变通

有时候编译器不够聪明

你想让编译器解决的问题相当于停止问题。由于该问题可证明不能由计算机程序解决,我们只做最小的尝试来解决它。我们不做任何特别复杂的事情。你只能忍受了。

有关为什么程序分析等同于停止问题的更多信息,请参阅我关于推断方法的终点是否可达的主题的文章。这本质上与确定一个变量是否被明确赋值是一样的问题;分析非常相似。

http://blogs.msdn.com/b/ericlippert/archive/2011/02/24/never-say-never-part-two.aspx

如果您必须对没有默认构造函数的非空结构体执行此操作,该怎么办?

没有这种动物。所有的结构体,无论是否为空,都有一个默认构造函数。

如果start不是一个简单的DateTime对象,会有办法解决这个问题吗?

表达式default(T)为您提供任何类型t的默认值。您总是可以输入

Foo f = default(Foo);

并具有合法赋值。如果Foo是值类型,则调用默认构造函数,默认构造函数始终存在。如果是引用类型,则返回null

编译器没有办法知道因为gap变量你保证设置DateTime

直接用

DateTime start = DateTime.Now;

并完成它。

编辑更好的是,再次浏览你的代码时,使用

DateTime start = dtFrom;

struct中没有默认构造函数这样的东西。试一试:

struct MyStruct {
    public MyStruct() {
        // doesn't work
    }
}

可以有静态构造函数,但是不能为struct定义默认构造函数。这就是为什么在很多结构体上都有静态方法Create,以及为什么你可以用new Point()来代替Point.Empty

任何struct的"默认构造函数"总是将其所有字段初始化为默认值。某些类型的Empty静态字段是为了方便。这实际上对性能没有任何影响因为它们都是值类型

在我看来,你的bool gap和DateTime start实际上是一样的。试着这样重构:

DateTime? gapStart = null ;
for (int i = 0; i < totaldays; i++)
{
    if ( gapStart.HasValue )
    {
        if (list[i])
        {
            var whgap  = new WorkHistoryGap();
            whgap.From = gapStart.Value ; //unassigned variable error
            whgap.To   = dtFrom.AddDays(i);
            return whgap;
        }
    }
    else
    {
        gapStart = dtFrom.AddDays(i);
    }
}

[编辑注:请邮政编码样本,将…哦…]实际上编译。它使它更容易。

[进一步编辑注意:您将gap设置为true,并在第一次通过循环时设置start值。进一步重构如下:]

DateTime gapStart = dtFrom.AddDays( 0 );
for ( int i = 1 ; i < totaldays ; i++ )
{
  if ( list[i] )
  {
    var whgap  = new WorkHistoryGap();
    whgap.From = gapStart.Value; //unassigned variable error
    whgap.To = dtFrom.AddDays( i );
    return whgap;
  }
}

为什么要尝试绕过语言的设计?即使编译器可以提前计算出整个循环(这对编译器来说似乎是不必要的复杂),它如何知道不能在部分代码中抛出异常呢?你必须给start赋值,因为你稍后会在代码中使用它,可能在它(根据你的)不可避免的赋值之前。