якщо оператори, що відповідають декільком значенням


79

Будь-який простіший спосіб написати це твердження if?

if (value==1 || value==2)

Наприклад ... в SQL ви можете сказати where value in (1,2)замість where value=1 or value=2.

Я шукаю щось, що могло б працювати з будь-яким базовим типом ... string, int тощо.


2
Простіше - це особисті переваги. Особисто я не думаю, що це стає легшим value == 1 || value == 2.
Джоел Етертон,

1
@ Джоел: Це легко, але не інтуїтивно для того, хто звик говорити "Я хочу, щоб це було 1 або 2". Як виявляється, існували мови, які реалізують саме цей синтаксис; Мова запитів IBI FOCUS для одного.
Steven Sudit

@Steven Sudit - "Інтуїтивне" в цьому контексті зводиться до особистих уподобань.
Джоел Етертон,

@Steven Sudit - Маю визнати, я поки що досить розважений деякою смішною довжиною, і деякі відповіді збираються просто вирішити просту або.
Джоел Етертон,

5
Це лише базовий приклад чогось набагато складнішого. Я не просто працюю з двома значеннями. Я також намагаюся полегшити читання деяких із цих рядків. У деяких випадках у мене є оператори if, які заглиблюються занадто далеко і містять десятки можливих значень та довгі імена змінних.
Рікі,

Відповіді:


150

Як щодо:

if (new[] {1, 2}.Contains(value))

Це хак :)

Або якщо ви не проти створити власний метод розширення, ви можете створити наступне:

public static bool In<T>(this T obj, params T[] args)
{
    return args.Contains(obj);
}

І ви можете використовувати його так:

if (1.In(1, 2))

:)


11
Я не думаю , що перше речення дійсно хак як такої, здається , досить елегантно до мене, особливо якщо це код у фоновому режимі , який більшість людей називають з допомогою IsOneOfACertainType(typeToCheck);, наприклад
курники

8
Не забудьте включити using System.Linq;для використання Contains.
Guilherme de Jesus Santos

2
Це справді схоже на Python!
Panzercrisis

36

Більш складний спосіб :), який імітує SQL 'IN':

public static class Ext {    
    public static bool In<T>(this T t,params T[] values){
        foreach (T value in values) {
            if (t.Equals(value)) {
                return true;
            }
        }
        return false;
    }
}

if (value.In(1,2)) {
    // ...
}

Але йдіть на стандартний шлях, він більш читабельний.

EDIT : краще рішення, згідно з пропозицією @ Kobi:

public static class Ext {    
    public static bool In<T>(this T t,params T[] values){
        return values.Contains(t);
    }
}

потрібен тип повернення, але метод розширення - це те, що я збирався також запропонувати
Daniel DiPaolo

+1. Приємне рішення, як і моє, але ваше за допомогою Generics. Я заміню свій на ваш :)
збирається

Мені дуже подобається ця версія. Я припустив, що мені, можливо, доведеться побудувати власний метод.
Рікі,

7
Ви не могли написати return values.Contains(t)? Або return values.Any(v => t.Equals(v))?
Кобі


7

В якості альтернативи, і це дасть вам більшу гнучкість, якщо в майбутньому для тестування значень, відмінних від 1 або 2, буде використано оператор switch

switch(value)
{
case 1:
case 2:
   return true;
default:
   return false
}

Я навіть не сказав би "альтернативно", це найближча аналогія до використання IN в SQL зі списком, наведеним у прикладі ( Contains()тощо, що більше відповідає IN із результатами підзапиту).
Джон Ханна,

6

Якщо у вас є Список, ви можете використовувати .Contains (yourObject), якщо ви просто шукаєте його існуючим (наприклад, де). В іншому випадку подивіться на метод розширення Linq .Any ().


5

Використовуючи Linq,

if(new int[] {1, 2}.Contains(value))

Але я повинен думати, що ваш оригінал швидше.


Різниця в ефективності між цими двома майже не має значення. Оригінал більш читабельний та ідіоматичний. Звичайно, можна писати Enumerable.Range(0, 10).ToList().ForEach(x => Console.WriteLine(x));замість цього, for(int i = 0; i < 10; i++) { Console.WriteLine(i); }але це просто розсердить людей. "Ніхто ніколи не пише, хай 6буде групою".
jason

@ Джейсон - Я згоден, що якщо я побачу те, що я написав у виробничому коді, я буду злий. Це було більше "одним рядком, щоб пояснити спосіб це зробити", ніж "точно слідувати цьому". Дійсно, я погоджуюсь з людьми, які пропонують заяви про перехід.
Джоел Рондо,

5

Якщо ви багато разів шукаєте значення у фіксованому списку значень у довгому списку, слід використовувати HashSet <T> . Якщо список дуже короткий (<~ 20 елементів), Список може мати кращу продуктивність, виходячи з цього тесту HashSet проти продуктивності списку

HashSet<int> nums = new HashSet<int> { 1, 2, 3, 4, 5 };
// ....
if (nums.Contains(value))

:) Це цікаве твердження про HashSet... Враховуючи, що його названо на честь реалізації системи зберігання даних, чому, на вашу думку, воно якось пов’язане з BST?
Олексій Левенков 21.03.18

@AlexeiLevenkov :) Дякую, що вказали на це. Я здійснив деякий пошук, .NET-версія HashSet просто базується на лінійних алгоритмах. Для порівняння, у HashMap Java 8 використовується самобалансуючий BST.
деталь

3

Як правило, ні.

Так, бувають випадки, коли перелік належить до Arrayабо List, але це не загальний випадок.


2

Такий метод розширення зробив би це ...

public static bool In<T>(this T item, params T[] items)
{
    return items.Contains(item);
}

Використовуйте його так:

Console.WriteLine(1.In(1,2,3));
Console.WriteLine("a".In("a", "b"));

1

Простіше - це суб’єктивно, але, можливо, оператор перемикання буде простішим? Вам не потрібно повторювати змінну, тому на рядок може поміститися більше значень, а рядок із багатьма порівняннями є більш розбірливим, ніж аналог, який використовує оператор if.


1

У vb.net або C # я міг би очікувати, що найшвидшим загальним підходом для порівняння змінної з будь-якою розумною кількістю окремо названих об'єктів (на відміну, наприклад, від усіх речей у колекції) буде просто порівняння кожного об'єкта з порівнянним набагато як ви зробили. Безумовно, можна створити екземпляр колекції та перевірити, чи містить він об’єкт, і це може бути виразніше, ніж порівняння об’єкта з усіма елементами окремо, але якщо хтось не використовує конструкцію, яку компілятор може явно розпізнати, такий код майже напевно буде набагато повільнішим, ніж просто проведення індивідуальних порівнянь. Я б не хвилювався про швидкість, якщо код за своєю природою працюватиме щонайбільше кілька сотень разів на секунду, але я би з обережністю ставився до того, щоб код був замінений на щось таке, що '

Альтернативний підхід, якщо змінна є чимось на зразок типу перелічення, полягає у виборі значень перерахування потужності двох, щоб дозволити використання бітових масок. Якщо тип перерахування має 32 чи менше допустимих значень (наприклад, починаючи Гаррі = 1, Рон = 2, Герміона = 4, Джіні = 8, Невілл = 16), можна зберегти їх у цілому числі та перевірити наявність кількох бітів одночасно операція ((if ((thisOne & (Harry | Ron | Neville | Beatrix))! = 0) / * Зробіть щось * /. Це дозволить швидкий код, але обмежується переліками з невеликою кількістю значень.

Дещо потужніший підхід, але такий, який слід використовувати обережно, полягає у використанні деяких бітів значення для позначення атрибутів чогось, тоді як інші біти ідентифікують елемент. Наприклад, біт 30 може вказувати на те, що персонаж є чоловіком, біт 29 може означати друга Гаррі тощо, тоді як нижні біти розрізняють символи. Цей підхід дозволить додавати персонажів, які можуть бути друзями Гаррі, а можуть і не вимагати, не вимагаючи зміни коду, який перевіряє друзя Гаррі. Одне застереження при цьому полягає в тому, що потрібно розрізняти константи перерахування, які використовуються для ВСТАНОВЛЕННЯ значення перелічення, та ті, що використовуються для ТЕСТУВАННЯ. Наприклад, щоб встановити змінну для позначення Гаррі, можна встановити значення 0x60000001, але щоб побачити, чи є змінна Гаррі, слід перевірити її бітом за допомогою 0x00000001.

Ще один підхід, який може бути корисним, якщо загальна кількість можливих значень є помірним (наприклад, 16-16 000 або близько того), - мати масив прапорів, пов’язаних із кожним значенням. Потім можна кодувати щось на зразок "if ((((characterAttributes [theCharacter] & chracterAttribute.Male)! = 0)".) Цей підхід буде найкращим чином працювати, коли кількість символів досить мала. Якщо масив занадто великий, помилки кешу можуть сповільнитися знизити код до такої міри, що тестування проти невеликої кількості символів окремо було б швидшим.


1

Використання методів розширення:

public static class ObjectExtension
{
    public static bool In(this object obj, params object[] objects)
    {
        if (objects == null || obj == null)
            return false;
        object found = objects.FirstOrDefault(o => o.GetType().Equals(obj.GetType()) && o.Equals(obj));
        return (found != null);
    }
}

Тепер ви можете зробити це:

string role= "Admin";
if (role.In("Admin", "Director"))
{ 
    ...
} 

0
public static bool EqualsAny<T>(IEquatable<T> value, params T[] possibleMatches) {
    foreach (T t in possibleMatches) {
        if (value.Equals(t))
            return true;
    }
    return false;
}
public static bool EqualsAny<T>(IEquatable<T> value, IEnumerable<T> possibleMatches) {
    foreach (T t in possibleMatches) {
        if (value.Equals(t))
            return true;
    }
    return false;
}

-1

У мене була та сама проблема, але я вирішив її за допомогою перемикача оператора switch (значення, яке ви вмикаєте) {випадок 1: код, який ви хочете здійснити; випадок 2: код, який ви хочете здійснити; за замовчуванням: повернути значення}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.