处理NULL的最佳方法

时间:2010-09-28 13:03:46

标签: c# null resharper

在我的函数的顶部,我正在尝试以最好的方式处理在C#中进入我的过程的null。检查和处理null的最佳方法是什么?为什么?我已经添加了我正在使用的完整代码,Resharper告诉我使用选项#1。通常我按照它说的做,因为我理解为什么它会使它更有效率。这一次虽然我不确定所以我必须问。

Option #1
if (sender == null) return;

// Code goes here

or

Option #2
if (sender != null)
{ 
     // Code goes here
}

Complete Code
        private void EmployeeMouseHoverToolTip(object sender, EventArgs e)
        {
            if (sender != null)
            {
                var sUserIdentifier = ((C1TextBox)sender).Text;
                var userIdentifier = Guid.Empty;
                if (Utilities.IsGuid(sUserIdentifier))
                {
                    userIdentifier = new Guid(sUserIdentifier);
                }

                var toolTipText = Utilities.UserIdentifierToName(userIdentifier);
                c1SuperTooltip.SetToolTip(sender as C1TextBox, toolTipText);
            }
        }

18 个答案:

答案 0 :(得分:43)

最好的代码是禁止null (而不是你正在做的事情)。这并不总是可行的(有时以有意义的方式处理null很重要) - 但在大多数情况下都是如此。

然后您需要做的(在防御性编码中)是添加null检查并抛出异常:

if (arg == null)
    throw new ArgumentNullException("arg");

.NET框架和良好的库中的许多(如果不是大多数)方法都是这样做的。

除此之外,事件的sender应该永远null,并且我会说检查它是多余的。如果null传递给此事件,那么您的代码就会出现严重问题。

你处理null的方式(通过无声地吞下它并无所事事)可能会掩盖应用程序中的严重错误,并且很少(如果有的话)适当。代码中的错误应引起可疑行为,而不是席卷地毯。

答案 1 :(得分:11)

为什么不假装永远不会发生空引用,并且不捕获NullPointerException?

您获得了堆栈跟踪,大量信息,并将其作为例外处理。

答案 2 :(得分:9)

在我看来,resharper正在建议选项1,因为它使得更容易阅读代码。你最终会得到:

  • 减少缩进
  • 断言其要求并在方法顶部对它们做出反应的代码(如果发件人为空,我立即返回)
  • 通常更易于维护的代码,因为它更清晰

就性能而言,可能没什么区别(尽管对你来说很重要,测量它)。无论如何,没有什么可以阻止JIT编译器将一个表单重写到另一个表单,如果他们不能通过C#编译器输出相同的MSIL。

答案 3 :(得分:7)

这是一个事件处理程序,只应由控件调用以响应事件(从不直接由您自己的代码),因此您不应该关心空检查甚至是sender上的类型检查参数(如果您只将此事件处理程序附加到相同类型的控件)。我会这样做:

private void EmployeeMouseHoverToolTip(object sender, EventArgs e) {  
  var txtBox = (C1TextBox)sender;
  var sUserIdentifier = txtBox.Text;
  var userIdentifier = Utilities.IsGuid(sUserIdentifier) ? 
    new Guid(sUserIdentifier) : 
    Guid.Empty;
  var toolTipText = Utilities.UserIdentifierToName(userIdentifier);
  c1SuperTooltip.SetToolTip(txtBox, toolTipText);
}

实际上,我更进一步,将逻辑分开,从逻辑中获取工具提示文本,以便读取和更新UI。像这样:

private void EmployeeMouseHoverToolTip(object sender, EventArgs e) {  
  var txtBox = (C1TextBox)sender;
  var toolTipText = ResolveUpdatedTooltipText(txtBox.Text);
  c1SuperTooltip.SetToolTip(txtBox, toolTipText);
}

private string ResolveUpdatedTooltipText(string sUserIdentifier) {
  var userIdentifier = ResolveGuid(sUserIdentifier);
  return Utilities.UserIdentifierToName(userIdentifier);
}

private Guid ResolveGuid(string sUserIdentifier) {
  return Utilities.IsGuid(sUserIdentifier) ? 
    new Guid(sUserIdentifier) : 
    Guid.Empty;
}

因此,您不应使用您提供的任何选项。

答案 4 :(得分:5)

不要检查它。

如果你得到空值,你已经将处理程序添加到了你不应该拥有的东西中。如果其他一些错误导致它,你应该使用WinForms的全局异常处理程序来处理它,这样程序就不会轰炸,记录它,并将日志上传到你的站点,无论你怎样检查对于这样的错误。

答案 5 :(得分:3)

我个人更喜欢第一个选项

if (sender == null) return;

它减少了嵌套并提高了可读性。

答案 6 :(得分:1)

我通常选择#1选项。我觉得它更干净,意思更清晰。无论谁阅读代码,都知道如果我们已经安全地通过了空检查并且已经退出,那么我们就没有机会在稍后的发件人中弄乱空值。

答案 7 :(得分:1)

Resharper喜欢选项1,因为它是一个先决条件检查程序。如果不满足前提条件,则提前退货。

通常,早期返回对代码可读性具有破坏性,但在这种情况下,它是非常易读的。

通过这种方式,您可以轻松添加额外的前置条件检查,例如检查EventArgs e的内容,而无需重做主函数代码。

答案 8 :(得分:1)

我更喜欢

if (sender == null) return;

使用它,代码中的嵌套操作较少,如果存在null,则提前退出。

答案 9 :(得分:0)

您可以在将值赋给对象时使用DBNull类...

UserName = DBNull.Value != reader["UserName"] ? reader["UserName"].ToString() : default(string);

答案 10 :(得分:0)

我没有为私有方法处理null,因为我总是确保不会将null值发送到我的私有方法。如果出现问题并且null已经传递给私有方法,那么异常将从应该抛出,我将知道我做错了什么。如果你总是检查私有方法的空值,你可能会在运行时跳过一些逻辑错误,你永远不会知道你的代码中有一个,直到它在生产中遇到你。

答案 11 :(得分:0)

选项#1的变体,可以立即返回,也可以抛出异常。诀窍是知道使用哪种方法。我的经验法则是,如果它是公共接口的一部分,那么抛出异常。如果它是您在框架中深入控制的东西,那么只需立即返回并处理该级别的空引用。

public void IHaveNoControlOverWhereThisMethodIsCalled(object arg)
{
    if(arg == null)
        throw new ArgumentNullException("arg");    
}

private void TheOnlyCallersOfThisMethodComeFromMe(object arg)
{
    //I should do all my public parameter checking upstream and throw errors
    //at the public entry point only.
    if(arg == null)
         return;

}

在事件处理程序的特定情况下:

private void EmployeeMouseHoverToolTip(object sender, EventArgs e)
{
    var txtSender = sender as C1TextBox;
    if(txtSender == null) return;

    var sUserIdentifier = txtSender.Text;
    var userIdentifier = Guid.Empty;
    if (Utilities.IsGuid(sUserIdentifier))
    {
        userIdentifier = new Guid(sUserIdentifier);
    }

    var toolTipText = Utilities.UserIdentifierToName(userIdentifier);
    c1SuperTooltip.SetToolTip(sender as C1TextBox, toolTipText);
}

答案 12 :(得分:0)

选项#1我猜想会降低Cyclomatic的复杂性。在选项#2中,如果有另一个if条件,则它将归入if子句并增加复杂性

答案 13 :(得分:0)

性能影响很小,所以我甚至不担心。选项1更好,因为它更具可读性和更少阴天......

更具可读性,因为条件不会被否定,并且没有不必要的范围块。

答案 14 :(得分:0)

如果你不打算处理null发送者,那么我会选择第一个选项(并确保它是处理程序中的第一行,这样它就不会被其他代码隐藏)。

如果你认为你最终可以处理一个null发送者,我会选择第二个选项,因为它为你提供了一个更好的方法来处理事情以及为处理程序维护一个返回点。

答案 15 :(得分:0)

在这里的确切示例中,请查看您正在执行的操作的反转,因此如果sender为null,请提前退出。它读得更好(IMO)并导致更少的嵌套。

这个例子中的选项#1

答案 16 :(得分:-1)

根据ILDASM的说法,我会说选项#2(稍微)更高效:

代码:

class Program
{
    static void Main(string[] args)
    {
        Method1(null);
        Method2(null);
    }

    static void Method1(object sender)
    {
        if (sender == null)
            return;

        for (int x = 0; x < 100; x++)
        {
            Console.WriteLine(x.ToString());
        }
    }

    static void Method2(object sender)
    {
        if (sender != null)
        {
            for (int x = 0; x < 100; x++)
            {
                Console.WriteLine(x.ToString());
            }
        }
    }
}

方法1的ILDASM:

.method private hidebysig static void  Method1(object sender) cil managed
{
  // Code size       47 (0x2f)
  .maxstack  2
  .locals init ([0] int32 x,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldnull
  IL_0003:  ceq
  IL_0005:  ldc.i4.0
  IL_0006:  ceq
  IL_0008:  stloc.1
  IL_0009:  ldloc.1
  IL_000a:  brtrue.s   IL_000e
  IL_000c:  br.s       IL_002e
  IL_000e:  ldc.i4.0
  IL_000f:  stloc.0
  IL_0010:  br.s       IL_0025
  IL_0012:  nop
  IL_0013:  ldloca.s   x
  IL_0015:  call       instance string [mscorlib]System.Int32::ToString()
  IL_001a:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001f:  nop
  IL_0020:  nop
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.1
  IL_0023:  add
  IL_0024:  stloc.0
  IL_0025:  ldloc.0
  IL_0026:  ldc.i4.s   100
  IL_0028:  clt
  IL_002a:  stloc.1
  IL_002b:  ldloc.1
  IL_002c:  brtrue.s   IL_0012
  IL_002e:  ret
} // end of method Program::Method1

方法2的ILDASM:

.method private hidebysig static void  Method2(object sender) cil managed
{
  // Code size       44 (0x2c)
  .maxstack  2
  .locals init ([0] int32 x,
           [1] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldnull
  IL_0003:  ceq
  IL_0005:  stloc.1
  IL_0006:  ldloc.1
  IL_0007:  brtrue.s   IL_002b
  IL_0009:  nop
  IL_000a:  ldc.i4.0
  IL_000b:  stloc.0
  IL_000c:  br.s       IL_0021
  IL_000e:  nop
  IL_000f:  ldloca.s   x
  IL_0011:  call       instance string [mscorlib]System.Int32::ToString()
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001b:  nop
  IL_001c:  nop
  IL_001d:  ldloc.0
  IL_001e:  ldc.i4.1
  IL_001f:  add
  IL_0020:  stloc.0
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.s   100
  IL_0024:  clt
  IL_0026:  stloc.1
  IL_0027:  ldloc.1
  IL_0028:  brtrue.s   IL_000e
  IL_002a:  nop
  IL_002b:  ret
} // end of method Program::Method2

答案 17 :(得分:-1)

与@Konrad相似,但我喜欢在代码

的情况下最终添加异常的想法
if( sender != null )
{
    // Code goes here
    ....
} else
    throw new ArgumentNullExcpetion("sender");

所以我投票选择#strong <2>