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...

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
}

@ 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) {
    ...

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


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, отслеживающей это изменение.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.