如何处理未初始化的本地变量

5
阅读this Eric Lippert的文章后,我了解到C#编译器不喜欢我们留下未初始化的本地变量。由于我时常遇到这个“问题”,我查看了一些旧代码,并且能够清除大部分情况下其实不需要未初始化(SomeClass obj = null)本地变量的情况。但是我遇到了一种情况,不知道如何重构代码。
public void DoSomething(string foo) {   

    SomeClass obj; // = null; 

    try {
        obj = SomeClass.CreateItem(target);
    } catch(CustomException ex) {
        // notify UI of error
    }

    if (obj != null) {
        // do something with `obj`
    }
}
SomeClass.CreateItem 可能由于外部因素而失败。如果失败,我想通知用户;如果没有失败,我想执行一个操作。
C# 编译器不希望我将 obj 未初始化,所以我通常将其赋值为 null
现在这感觉像是一种“hack”,我的问题是:
上面的代码存在设计缺陷吗?
如果存在,当我无法确定引用在运行时是否指向现有对象时,我应该如何处理编译时的引用?

1
编译器的“推理”是,如果SomeClass.CreateItem中出现异常,则在到达“obj!= null”测试时,obj将保持未初始化状态。一般原则是,如果变量在代码中必须被赋值,即所有条件分支都分配了一个值,则不需要初始化变量。 - Graffito
3个回答

10

我会像这样重构代码:

private SomeClass TryToCreateItem()
{
    try 
    {
        return SomeClass.CreateItem(target);
    } 
    catch(CustomException ex) 
    {
        // notify UI of error
    }
    return null;
}

public void DoSomething(string foo) 
{ 
    SomeClass obj = TryToCreateItem(); 
    if (obj != null) {
      // do something with `obj`
    }
"

“提炼函数”是我最喜欢的重构技巧。

"

难道不是出于类似的原因,你会重构“bool TryParse(string input, out int result)”吗?还是说这是一个不同的情况,因为TryParse通常处理(非空)值类型? - MrPaulch
1
@MrPaulch:你提出了一个很好的观点;这里的null被用作操作失败的信号。如果你需要区分三种情况:失败/null、成功/null和成功/非null,那么你需要一些额外的机制。 - Eric Lippert

2
< p > // 省略部分代码 obj`` 代码,应该在try块内

你尝试的是运行一些可能成功或失败的代码,然后只有在前面的代码成功后才运行其他代码。
这通常是另一个逻辑块的一部分,其依赖于没有异常。如果在构造此对象时发生异常,则需要跳过此代码,而将其包括在try块中正好可以获得此行为。


1
如果//do something部分的某些部分也能抛出CustomException,那该怎么办?那么仅通过捕获就无法确定出错原因。本质上,我心中存在一种冲突的约定,即:“尽可能紧凑地保持您的try块”。 - MrPaulch
@MrPaulch,如果其他情况也会抛出相同的异常,您真的想要执行完全不同的操作吗?如果是这样,那么这表明它们不应该同时抛出相同的异常。我不认为尝试保持try块紧凑是一个好主意。相反,当您看到非常紧密的try块时,这表明某些地方出了问题。它们通常应该覆盖表示某个单一逻辑操作的所有操作。 - Servy
你提到“不同”的异常很有道理!最后一个问题是:如果我需要返回对象(如果存在)或 null(如果不存在),我该怎么办? - MrPaulch
@MrPaulch 在 try 中返回它,并在 catch 中返回 null - Servy

0

你可以重构代码,将所有与该对象相关的 try/catch 封装起来,如果确实需要在失败时执行某些操作,则可以使用布尔值将其与代码的其余部分关联:

    public void DoSomething(string foo)
    {

        bool failedCreation = false;

        try
        {
            SomeClass obj = SomeClass.CreateItem(target);
        }
        catch (CustomException ex)
        {
            // notify UI of error
            failedCreation = true;
        }

        if (failedCreation)
        {
            // do other stuff.
        }
    }

但这似乎不是你想要的。我会将所有内容封装在try/catch中并完成它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接