Подтвердить что ты не робот

Лучший способ обработки NULL

В верхней части моих функций я пытаюсь наилучшим образом обработать нуль, входящий в мои процедуры на С#. Каков наилучший способ проверки и обработки нулевого и почему? Я добавил полный код того, что я использую прямо сейчас, и Resharper говорит мне использовать Option # 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);
            }
        }
4b9b3361

Ответ 1

Это обработчик событий, он должен вызываться только элементами управления в ответ на событие (никогда не напрямую с помощью собственного кода), поэтому вам не нужно заботиться о нулевых проверках или даже проверять тип в параметре 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);
}

На самом деле, я бы сделал еще один шаг и разделил логику, чтобы получить текст всплывающей подсказки из логики, чтобы читать и обновлять пользовательский интерфейс. Что-то вроде этого:

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;
}

Следовательно, вы не должны использовать какие-либо параметры, которые вы предоставили.

Ответ 2

Лучший код - запретить null (вместо того, что вы делаете). Это не всегда возможно (иногда его важно обрабатывать null значимым образом), но в большинстве случаев это так.

Тогда все, что вам нужно сделать (в защитном кодировании), это добавить проверку null и исключить исключение:

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

Многие (если не большинство) методы в .NET Framework и в хороших библиотеках делают это именно так.

Кроме того, sender события должен никогда быть null, а Id сказать, что проверка на него является избыточной. Если null передается этому событию, что-то серьезно не соответствует вашему коду.

То, как вы обрабатываете null (молча проглатывая его и ничего не делая), может маскировать серьезные ошибки в приложении и редко, если вообще когда-либо, подходит. Ошибки в коде должны вызывать подозрительное поведение, а не подметаться под ковер.

Ответ 3

Почему бы просто не притворяться, что нулевая ссылка никогда не возникает, и не поймать исключение NullPointerException?

Вы получаете трассировку стека, большое количество информации и обрабатываете ее как исключение.

Ответ 4

Вариант 1 предлагается с помощью resharper, на мой взгляд, потому что он упрощает чтение кода. Вы получите:

  • Меньше отступов
  • Кодекс, который утверждает свои требования и реагирует на них прямо вверху метода (если отправитель имеет значение null, я возвращаюсь сразу)
  • Код, который обычно проще поддерживать, потому что он более понятный

Что касается производительности, то, вероятно, мало различий (хотя, если это важно для вас, измерьте ее). Там ничто не мешает компилятору JIT переписывать одну форму в другую в любом случае, если они все равно не получат в качестве идентичного MSIL компилятором С#.

Ответ 5

Не проверяйте его.

Если вы получаете null, вы добавили обработчик к чему-то, чего у вас не должно быть. И если какая-то другая ошибка вызывает это, вы должны обрабатывать его с помощью обработчика глобальных исключений WinForms, чтобы программа не бомбила, не регистрировала его и не загружала журналы на ваш сайт, как бы вы ни пытались проверить такие ошибки.

Ответ 6

Я лично предпочитаю первый вариант

if (sender == null) return;

Он сокращает вложенность и повышает читаемость.

Ответ 7

Обычно я использую Option # 1. Я чувствую, что он чище, и смысл этого более ясен. Тот, кто читает код, знает, что если мы безопасно прошли мимо нулевой проверки и вышли, тогда нет никаких шансов, что мы начнем с нулевого значения в отправителе позже.

Ответ 8

Я предпочитаю

if (sender == null) return;

с ним в коде меньше вложенных операций и ранний выход, если есть нуль.

Ответ 9

Resharper нравится вариант 1, поскольку он является предварительным условием проверки. Когда предварительные условия не выполняются, выполняется раннее возвращение.

Обычно раннее возвращение является деструктивным для чтения кода, но в этом случае оно очень читаемо.

Таким образом, вы можете легко добавить дополнительные проверки перед установкой, например, проверить содержимое EventArgs e, не перерабатывая основной код функции.

Ответ 10

В вашем конкретном примере здесь перейдите к обратному тому, что вы делаете, поэтому, если отправитель равен нулю, перейдите на ранний выход. Он лучше читает (ИМО) и приводит к меньшему гнезду.

Итак, вариант # 1 в этом случае

Ответ 11

Если вы не собираетесь обрабатывать нулевой отправитель, я бы пошел с первым параметром (и убедитесь, что это первая строка в обработчике, чтобы он не скрывался другим кодом).

Если вы думаете, что в конечном итоге вы можете обработать нулевого отправителя, я бы пошел со вторым вариантом, так как он предлагает вам лучший способ обработки вещей, а также сохранение единственной точки возврата для обработчика.

Ответ 12

Влияние производительности минимально, поэтому я даже не буду об этом беспокоиться. Вариант 1 лучше, потому что он более читабельный и менее облачный...

Более читаемый, поскольку условие не отрицается, и нет ненужного блока видимости.

Ответ 13

Вариант №1, я думаю, это уменьшит сложность Cyclomatic. В параметре № 2, если есть другие условия if, он попадает под предложение if и увеличивает сложность

Ответ 14

Вариант варианта №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);
}

Ответ 15

Я не обрабатываю null для частных методов, так как я всегда убеждаюсь, что никакие нулевые значения не будут отправлены моим личным методам. Если что-то пошло не так, и null был передан частному методу, поэтому исключение будет выбрано из того, что должно быть, и я буду знать, что я сделал что-то неправильно. Если вы всегда проверяете нулевое значение для частного метода, вы можете пропустить некоторую логическую ошибку во время выполнения, и вы никогда не узнаете, что у вас есть один код, пока он не ударит вас в процессе производства.

Ответ 16

вы можете использовать класс DBNull при назначении значения объекту...

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

Ответ 17

Основываясь на том, что говорит 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());
            }
        }
    }
}

ILDASM для метода 1:

.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

ILDASM для метода 2:

.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

Ответ 18

Подобно @Konrad, но мне нравится идея добавить исключение в конце, если код

if( sender != null )
{
    // Code goes here
    ....
} else
    throw new ArgumentNullExcpetion("sender");

Итак, я голосую за вариант # 2