C#null coalescing运算符返回null

时间:2011-02-01 20:06:43

标签: c# .net

最近,我的同事向我展示了一段无法正常运行的代码:

public class SomeClass
{
    private IList<Category> _categories;

    public void SetCategories()
    {
        _categories = GetCategories() ?? new List<Category>();
        DoSomethingElse();
    }

    public IList<Category> GetCategories()
    {
        return RetrieveCategories().Select(Something).ToList();
    }
}

(我知道运算符是多余的,因为linq ToList将始终返回一个列表,但这就是代码的设置方式。)

问题是_categories null 。在调试器中,在_categories = GetCategories() ?? new List<Category>()上设置断点,然后单步转到DoSomethingElse(),_ 类别仍为空。

直接将_categories设置为GetCategories()工作正常。拆分?在一个完整的if语句工作正常。空合并运算符没有。

这是一个ASP.NET应用程序,所以不同的线程可能会干扰,但它在他的机器上,只有他在浏览器中连接。 _cateogories不是静态的,或任何东西。

我想知道的是,怎么可能发生这种情况?

编辑:

只是为了增加奇怪性, _categories除了该函数之外从未设置过任何地方(除了初始化类之外)。

确切的代码如下:

public class CategoryListControl
{
    private ICategoryRepository _repo;
    private IList<Category> _categories;

    public override string Render(/* args */)
    {
        _repo = ServiceLocator.Get<ICategoryRepository>();
        Category category = _repo.FindByUrl(url);
        _categories = _repo.GetChildren(category) ?? new List<Category>();
        Render(/* Some other rendering stuff */);
    }
}

public class CategoryRepository : ICategoryRepository
{
    private static IList<Category> _categories;

    public IList<Category> GetChildren(Category parent)
    {
        return _categories.Where(c => c.Parent == parent).ToList<Category>();
    }
}

即使GetChildren神奇地返回null,CategoryListControl._categories仍然永远不应该为null。由于IEnumerable.ToList(),GetChildren也永远不会返回null。

编辑2:

试用@ smartcaveman的代码,我发现了这个:

Category category = _repo.FindByUrl(url);

_categories = _repo.GetChildren(category) ?? new List<Category>();

_skins = skin; // When the debugger is here, _categories is null

Renderer.Render(output, _skins.Content, WriteContent); // When the debugger is here, _categories is fine.

同样,在测试if(_categories == null) throw new Exception()时,_categories在if语句中为null,然后没有抛出异常。

所以,似乎这是一个调试器错误。

6 个答案:

答案 0 :(得分:2)

这可能是调试器的问题,而不是代码。尝试打印出值或在使用coalesce运算符的语句后执行空检查。

答案 1 :(得分:2)

null-coalescing运算符未被破坏。我一直都以类似的方式使用它。还有其他事情正在发生。

答案 2 :(得分:1)

如果您确定因为线程问题,那么请使用lock关键字。我相信这应该有效。

public class SomeClass
{
    private IList<Category> _categories;

    public void SetCategories()
    {
        lock(this) 
        {
          _categories = GetCategories() ?? new List<Category>();
          DoSomethingElse();
        }
    }

    public IList<Category> GetCategories()
    {
        return RetrieveCategories().Select(Something).ToList();
    }
}

答案 3 :(得分:1)

尝试进行干净的构建。构建菜单 - &gt;清理,然后再次调试。代码本身很好。

答案 4 :(得分:1)

(1)DoSomethingElse()可能会在出现错误之前将_categories字段设置为null。测试此方法的一种方法是使_categories字段只读。如果这是错误,那么您将收到编译器错误,即只读字段不能用作分配目标 (2)您的_categories字段是通过其他线程中的其他函数设置的。无论哪种方式,以下应该解决您的问题,或至少说清楚它的位置。

public class SomeClass
{
    private static readonly object CategoryListLock = new object();
    private readonly List<Category> _categories = new List<Category>();
    private bool _loaded = false;

    public void SetCategories()
    {
        if(!_loaded)
        {
            lock(CategoryListLock)
            {
                if(!_loaded)
                {
                    _categories.AddRange(GetCategories());
                    _loaded = true;
                }
            }
        }
        DoSomethingElse();
    }

    public IList<Category> GetCategories()
    {
        return RetrieveCategories().Select(Something).ToList();
    }
}

**看到您的修改后,看起来您有两个不同的字段IList<Category> _categories_categories中的CategoryListControl字段为空是没有意义的,但_categories类中的静态CategoryRepository看起来应该为null,这取决于你是什么意思发布。也许你对哪个字段引发错误感到困惑。我理解该行是在CategoryListControl中调用的,所以你的错误会说它在CategoryListControl类中,但实际的异常可能来自GetChildren()方法,该方法试图从空列表中创建子列表) 。由于这些字段的名称相同,因此很容易看出它们如何混淆。通过将_categories中的CategoryRepository字段设置为只读初始化字段来对此进行测试。

即使CategoryRepository中的_categories字段并非总是为null,它也可能受到我解释如何修复Control类**的任何线程问题的影响**

它确保您正在调试正确的_categories字段,试试这个。

    _categories = GetCategories() ?? new List<Category>();
    if(_categories == null){
          throw new Exception("WTF???");
     }
    DoSomethingElse();

如果你没有得到“WTF ???”的异常然后你就知道错误的根源在其他地方。

并且,关于Linq扩展:Where()和ToList()都不能返回null。如果任何参数为null,则两个方法都将抛出ArgumentNullException。我用反射器检查了这个。

请告诉我们您的结果。我现在也很好奇。

答案 5 :(得分:1)

这可能发生,因为您已启用优化 - 在这种情况下,只要编译器可以证明这样做不会更改结果,分配可能会延迟。当然,这在调试器中看起来很奇怪,但它完全没问题。