C #: ключевое слово is и проверка на Not

288

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

if (child is IContainer) { //....

Есть ли более элегантный способ проверить наличие экземпляра «НЕ»?

if (!(child is IContainer)) { //A little ugly... silly, yes I know...

//these don't work :)
if (child !is IContainer) {
if (child isnt IContainer) { 
if (child aint IContainer) { 
if (child isnotafreaking IContainer) { 

Да да ... глупый вопрос ....

Поскольку существует некоторый вопрос о том, как выглядит код, это просто простое возвращение в начале метода.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Hugoware
источник
105
Мне лично нравится "ребенок не сумасшедший ...". Я голосую за включение этого ключевого слова в C # 5
Джозеф
Мне интересно узнать ситуацию, в которой вы бы это использовали? Как выглядит «else» часть этого кода, и вы не можете просто инвертировать тест? Если в вашем коде написано «если child не является IContainer, то генерировать исключения» или «если child не является IContainer, то, возможно, это IFoo, так что я попробую это дальше», тогда нет ли там подразумеваемого выражения else? Я, наверное, что-то упустил.
Мартин Пек
1
@MartinPeck, не может быть еще условие. Вот почему я искал это.
Джошуа Уолш
@MartinPeck вот пример: if (!(argument is MapsControlViewModel vm)) { return; }- Я мог бы инвертировать if и поместить оставшуюся часть метода в квадратные скобки if, но тогда я получил бы рождественский код с множеством закрывающих скобок в конце метода. Это гораздо менее читабельно.
ANeves
может быть, нам нужны ifnotзаявления
Дэйв Кузино,

Ответы:

301
if(!(child is IContainer))

это единственный оператор (нет IsNotоператора).

Вы можете создать метод расширения, который делает это:

public static bool IsA<T>(this object obj) {
    return obj is T;
}

а затем использовать его для:

if (!child.IsA<IContainer>())

И вы можете следить за вашей темой:

public static bool IsNotAFreaking<T>(this object obj) {
    return !(obj is T);
}

if (child.IsNotAFreaking<IContainer>()) { // ...

Обновление (учитывая фрагмент кода ОП):

Поскольку вы на самом деле приводите значение потом, вы можете просто использовать asвместо этого:

public void Update(DocumentPart part) {
    part.Update();
    IContainer containerPart = part as IContainer;
    if(containerPart == null) return;
    foreach(DocumentPart child in containerPart.Children) { // omit the cast.
       //...etc...
Мехрдад Афшари
источник
1
ck: я имел ввиду в смысле операторов, тут ничего нет IsNot.
Мехрдад Афшари
5
Да. Я шучу, если это не очевидно.
Мехрдад Афшари
111

Вы можете сделать это следующим образом:

object a = new StreamWriter("c:\\temp\\test.txt");

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
Дальневосточные
источник
2
@Frank - да, ключевое слово is дает логическое значение, которое можно сравнить с false
cjk
32
@ Фрэнк это работает, потому что isимеет более высокий приоритет по сравнению с ==. Единственная причина, по которой вы не можете использовать !x is fэто то, что он имеет меньший приоритет, чем !.
Мехрдад Афшари
Мне это нравится, но, похоже, он не работает правильно, когда вводит переменную, хотя и должен. if (a is TextReader reader == false)«Должен» работать, но он не позволит вам использовать переменную в истинном пути, говоря, что она, возможно, не была инициализирована.
Дейв Кузино
@DaveCousineau - Обычно вы проверяете тип и вводите переменную, когда хотите использовать введенную переменную. Я не уверен, как переменная будет полезна, если проверка типа не удалась. (Отказ от ответственности - я нахожу, что функция «Сопоставление с шаблоном» имеет как плохое имя, так и плохой запах кода, как при использовании outпараметров)
StingyJack
@StingyJack есть некоторый сбой, когда в истинном пути переменная считается неинициализированной. даже если вы говорите, if (a is TextReader reader == true)что считает переменную неинициализированной.
Дэйв Кузино
11

Почему бы просто не использовать остальное?

if (child is IContainer)
{
  //
}
else
{
  // Do what you want here
}

Это аккуратно, это знакомо и просто?

Марк Бродхерст
источник
3
Ничего плохого в этом нет - это просто глупый вопрос. Я хотел немедленно выйти из функции, если что-то не было определенного типа. Я сделал это (! (Ребенок - что-то)) навсегда сейчас, но я подумал, что позабочусь, чтобы не было лучшего способа.
Hugoware
1
С примером кода в вопросе это будет означать пустую скобку if. Это не похоже на разумную альтернативу.
ANeves
9

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

public static bool Is<T>(this object myObject)
{
    return (myObject is T);
}

public static bool IsNot<T>(this object myObject)
{
    return !(myObject is T);
}

Тогда вы могли бы написать:

if (child.IsNot<IContainer>())
{
    // child is not an IContainer
}
Роберт Картейно
источник
8

Это еще не было упомянуто. Это работает, и я думаю, что это выглядит лучше, чем при использовании!(child is IContainer)

if (part is IContainer is false)
{
    return;
}

Новое в C # 9.0

https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/#logical-patterns

if (part is not IContainer)
{
    return;
}
Тодд Скелтон
источник
3
Точно так же вы могли бы сделать if (part as IContainer is null). Честно говоря, не уверен, что лучше.
Flynn1179,
5

Гадкий? Я не согласен. Единственный другой способ (лично я считаю, что это «уродливее»):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BFree
источник
@ Mehrdad - Nullable? позволит ему работать, а не то, что это должно быть использовано. Это просто пример более уродливого пути.
Stevehipwell
@ Steveo3000: Да, но вы должны явно упомянуть? это asпункт. obj as intвсегда ошибка времени компиляции.
Мердад Афшари
@Mehrdad - Согласен, BFree может отредактировать свой пост, чтобы отразить это. Дает нам «obj as int?».
Stevehipwell
@ Stevo3000: Однако я не думаю, что с этим что-то не так. IContainer выглядит как интерфейс, а не тип значения. Просто хочу отметить, что он требует осторожности с типом значения и не всегда является прямым переводом isформы.
Мердад Афшари
При желании вы можете сделать if (obj == default (IContainer)), который позаботится о типах значений и ссылочных типах
Джозеф
3

В isоператоре принимает значение булева результата, так что вы можете делать все , что бы в противном случае быть в состоянии сделать на BOOL. Чтобы отрицать это, используйте !оператор. Зачем вам нужен другой оператор только для этого?

Брайан Расмуссен
источник
5
Это не другой оператор. Мне было интересно, если бы было ключевое слово, которое позволило бы мне лишний набор паренсов. Это серьезный пример, но мне было любопытно.
Hugoware
Хорошо, я понял. Из ваших примеров у меня сложилось впечатление, что вы ищете нового, преданного своему делу оператора.
Брайан Расмуссен
Я думаю, что иметь такого специального оператора - это плохо, потому что у нас будет такой путь (объяснил это и так, во всяком случае), и если у нас был другой оператор, то есть два способа достичь того же, что может сбить с толку.
BuddhiP
3

Метод расширения IsNot<T>- это хороший способ расширить синтаксис. Иметь ввиду

var container = child as IContainer;
if(container != null)
{
  // do something w/ contianer
}

работает лучше, чем делать что-то вроде

if(child is IContainer)
{
  var container = child as IContainer;
  // do something w/ container
}

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

Джефф
источник
3

Хотя это не устраняет проблему с круглыми скобками, ради людей, попадающих сюда через Google, следует отметить, что существует более новый синтаксис (начиная с C # 7), чтобы сделать остальную часть вашего кода немного чище:

if (!(DocumentPart is IContainer container)) { return; }
foreach(DocumentPart child in container.Children) {
    ...

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

StriplingWarrior
источник
2

Хотя оператор IS обычно является наилучшим способом, есть альтернатива, которую вы можете использовать при некоторых обстоятельствах. Вы можете использовать оператор as и проверить нулевое значение.

MyClass mc = foo as MyClass;
if ( mc == null ) { }
else {}
Муад'Диб
источник
2

C # 9 (будет выпущен с .NET 5) будет включать в себя логические шаблоны and, orи not, что позволяет нам написать это более элегантно:

if (child is not IContainer) { ... }

Аналогично, этот шаблон можно использовать для проверки на нулевое значение:

if (child is not null) { ... }

Вы можете найти более подробную информацию о проблеме Github, отслеживающей это изменение.

Торкил Холм-Якобсен
источник