Должны ли мы проверять NULL при приведении iCLASS к классу?

#c#

#c#

Вопрос:

Вопрос 1> Должен ли я проверять NULL в следующем случае?

 public interface INewClass {}
public class NewClass : INewClass {}

public void FunMeA(INewClass obj)
{
    NewClass n = (NewClass) obj;
    ... // Should I check (n == null) here?
    // call methods defined inside INewClass (updated from NewClass to INewClass)
    ...
}

A concrete example,

public void FunMeB(IAsyncResult itfAR)
{
    AsyncResult ar = (AsyncResult) itfAR;
    ... // Should I check (ar == null) here?
    // access ar.AsyncDelegate
    ...
}
  

Вопрос 2> Я только начинаю переходить на C # с C .
При написании кода на C я знаю, когда следует выполнить проверку.
Однако я полностью потерялся в мире C #. Итак, вопрос в том, существует ли
общее правило, которое может подсказать мне, когда я ДОЛЖЕН проверять значение NULL?

Спасибо

Комментарии:

1. Если методы определены в интерфейсе INewClass, зачем вы вообще приводите?

2. Просто проверьте, равен ли объект NULL, прежде чем использовать функцию. Идеальная ситуация заключается в том, что к моменту использования метода объект не может иметь значения null, поскольку это требуется для функционирования вашей программы.

Ответ №1:

При выполнении этого:

 NewClass n = (NewClass) obj;
  

В этом нет смысла, потому что, если он не будет приведен, он выдаст недопустимое приведенное исключение.

Если у вас есть какие-либо сомнения относительно того, действительно ли вы можете его привести, вы хотите сделать:

 NewClass n = obj as NewClass;
  

затем

 if(n != null) ...
  

Выполняемое вами приведение называется прямым приведением, при котором система будет предполагать, что оно может быть выполнено. n = obj as NewClass это называется косвенным приведением и предназначено для тех сценариев, где вы хотите сообщить программе «Эй, я думаю, это сработает, но если нет, не переключайтесь и не создавайте исключение…Я разберусь с этим, если это не сработает.»

Использование is vs as при приведении.

В зависимости от того, какой сценарий вы хотите, один будет лучше другого. Технически с точки зрения производительности as предпочтительнее. .Net будет использовать атомарную попытку приведения к желаемому типу и возвращать null, если это не так, где с is ему придется дважды обойти дерево наследования, чтобы увидеть, соответствует ли оно этому типу, а затем привести его. Итак, в большинстве случаев, если вы хотите увидеть, хотите ли вы привести и использовать этот тип, лучше:

 var obj = o as type
if (obj != null)
  

в отличие от

 if(o is type)
{
   var obj = (type); 
}
  

Комментарии:

1. -1: Этот последний абзац не имеет большого смысла. Как вы думаете, как .NET определяет, является ли приведение допустимым с помощью as ключевого слова или (SomeType)variable синтаксиса? Очевидно, что это должно смотреть на дерево наследования. Документация ( msdn.microsoft.com/en-us/library/cscsdfbt.aspx ) говорит, что выражение as эквивалентно expression is type ? (type)expression : (type)null за исключением того, что выражение вычисляется только один раз. Кроме того, попробуйте посмотреть на IL. Я скомпилировал выражение return (value as TOut) != null; , а Reflector разобрал его как return (value is TOut);

2. … продолжение: Но IL для as оператора содержит 12 инструкций в отличие от 8 для is оператора.

3. Вы правы, это не имело особого смысла для ОС. Я это исправил.

4. 1 — -1 = 2 … Как тебе такое запутанное двойное отрицательное выражение!

Ответ №2:

Если приведение завершится неудачей, ваш код выдаст исключение.

Используйте is оператор, чтобы увидеть, сработает ли приведение, и as для приведения (которое не будет выдавать и возвращать значение null, если приведение завершится неудачей).

Итак:

 if(obj is NewClass)
{
  //Yay, can cast!
}
  

Или:

 NewClass nc = obj as NewClass;
if(nc != null)
{
  //Yay!
}
  

Комментарии:

1. Но не используйте ‘is’ и ‘as’ вместе. Если вы хотите обработать вероятность того, что приведение недопустимо, используйте ‘as’ с проверкой null.

2. Конечно. Я хочу сказать, что ‘if (x is SomeType) { var y = x as SomeType; … }’ является избыточным.

Ответ №3:

Если вы хотите проверить наличие null, вам следует сделать

 NewClass n = obj as NewClass ;
if (n != null)
{

...

}
  

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

Комментарии:

1. Это назначение не вызовет исключение NullReferenceException, если obj равно null: NewClass n = (NewClass)obj;

2. @phoog Я хотел сказать, что вы должны проверять значение null, только если вы точно знаете, почему оно равно null, и у вас есть средства справиться с ситуацией. Некоторые люди проверяют наличие null во всем, с чем они работают, и ничего не делают в части else. Это не приводит к исключению нулевого указателя во время выполнения, но когда что-то не так, трудно понять, что не так. В большинстве ситуаций лучше использовать прямое приведение, а не безопасное приведение.

3. Согласен. Очень неприятно, когда программы завершаются беззвучно.

Ответ №4:

Если вы это сделаете NewClass n = (NewClass)obj , вы получите исключение, если оно не будет приведено должным образом.

Если вы хотите, чтобы была возможность, чтобы оно было null, без создания исключения, вы бы хотели сделать, NewClass n = obj as NewClass; а затем проверить, n равно ли это null.

Вы можете проверить, сработает ли приведение заранее, используя is оператор. if(obj is NewClass)

Надеюсь, это поможет.

Комментарии:

1. Вы можете привести нулевую ссылку. Попробуйте, вам понравится.

Ответ №5:

Если параметр является обязательным, вы всегда должны проверять null и выдавать ArgumentException или ArugmentNullException .

Лучший способ проверки этого параметра здесь:

 AsyncResult ar = itfAR as AsyncResu<
if(ar == null)
{
    // even better: Use a resource here
    throw new ArgumentNullException("Parameter itfAR must not be null and must be of type AsyncResult"); 
}
  

Комментарии:

1. @q0987 Также есть ArgumentOutOfRangeException , но здесь это, конечно, не имело бы никакого смысла

2. Это может вызвать вводящее в заблуждение исключение. Например, если itfAR относится к типу, который реализует IAsyncResult, но не наследует от AsyncResult, вы выдадите исключение ArgumentNullException, даже если аргумент не был равен null. Сбивает с толку.

Ответ №6:

Я предпочитаю проверять значение to на null перед приведением. Например.

 if (obj == null) 
{
  throw new ArgumentNullException("obj", "The argument must has a value specified");
}
  

Ответ №7:

Вы путаете проблемы.

  1. Вы всегда должны проверять аргументы общедоступного метода. Поэтому вы должны сначала сделать:

    public WhateEverMethod(элемент IElement) { if (элемент IElement == null) { создать новое исключение ArgumentNullException(…); } }

  2. Как только вы подтвердите, что элемент не равен null, вы проверите, можете ли вы выполнить приведение. По сути, вы можете пойти двумя путями:

    Элемент element = IElement как элемент;

    if (element == null) //приведение не удалось { создать новое исключение InvalidCastException(…); }

или

 if (!(iElement is Element))
{
    throw new InvalidCastException(...);
}

Element element = (Element)iElement;
  

Комментарии:

1. Element element = (Element)(new Object()); в любом случае выдает InvalidCastException , так зачем проверять и выбрасывать его в первую очередь!?

2. Я поддерживаю точку зрения Саймона. Если недопустимое приведение является исключительным условием, нет смысла самостоятельно создавать недопустимое исключение приведения. Однако, когда я делаю это, я обычно добавляю комментарий, чтобы кто-нибудь другой не подумал, что я упустил из виду возможность недопустимого приведения и изменил мой код: var theString = (string)obj; // throws an exception if obj is not a string

3. @Simon: Верно, виноват. Я не думал ясно. Однако я хотел бы уточнить, что я бы различал ArgumentNull и InvalidCast . Но, очевидно, да, проверка приведения совершенно не нужна.