Может ли Objective-C включить NSString?


166

Есть ли более разумный способ переписать это?

if ([cardName isEqualToString:@"Six"]) {
    [self setValue:6];
} else if ([cardName isEqualToString:@"Seven"]) {
    [self setValue:7];
} else if ([cardName isEqualToString:@"Eight"]) {
    [self setValue:8];
} else if ([cardName isEqualToString:@"Nine"]) {
    [self setValue:9];
} 

1
Нет, переключатель работает только для типов int / bool / char / etc.
chown

этот вопрос чем-то похож на этот вопрос, который был опубликован только час назад ( stackoverflow.com/questions/8161319/… )
Майкл Даутерманн

3
Есть несколько альтернативных способов сделать это. Например, загрузить массив со значениями и найти совпадение в массиве. Ничего страшного, но они уменьшают дублирование кода.
Hot Licks

4
Как примечание, новый язык Apple (Swift) теперь позволяет сравнивать строки в операторе switch!
Джаредмит

4
Переключиться на Свифт;)
tothemario

Ответы:


147

К сожалению, они не могут. Это один из лучших и наиболее востребованных вариантов использования операторов switch, так что, надеюсь, они надеются на (сейчас) Java (и другие) победившую команду!

Если вы делаете имена карт, возможно, присвойте каждому объекту карты целочисленное значение и включите его. Или, возможно, enum, который считается числом и поэтому может быть включен.

например

typedef enum{
  Ace, Two, Three, Four, Five ... Jack, Queen, King

} CardType;

В таком случае Ace будет равен случаю 0, два - случаю 1 и т. Д.


4
@abbood Для получения дополнительной информации о enum, см. сообщение NS_ENUM & NS_OPTIONS от Mattt Thompson.
Базилик Бурк

@abbood, что должен означать твой комментарий? Похоже, это плохой ответ, но мне кажется, хорошо. Могли бы вы объяснить ?
Алан Андраде

Как я понимаю, CardTypeне может быть равным никаким вложенным, @""например:[CardType isEqualToString:@"Three"]
Adromil Balais

120

Вы можете настроить словарь блоков, например так:

NSString *lookup = @"Hearts"; // The value you want to switch on

typedef void (^CaseBlock)();

// Squint and this looks like a proper switch!
NSDictionary *d = @{
    @"Diamonds": 
    ^{ 
        NSLog(@"Riches!"); 
    },
    @"Hearts":
    ^{ 
        self.hearts++;
        NSLog(@"Hearts!"); 
    },
    @"Clubs":
    ^{ 
        NSLog(@"Late night coding > late night dancing"); 
    },
    @"Spades":
    ^{ 
        NSLog(@"I'm digging it"); 
    }
};

((CaseBlock)d[lookup])(); // invoke the correct block of code

Чтобы иметь раздел «по умолчанию», замените последнюю строку на:

CaseBlock c = d[lookup];
if (c) c(); else { NSLog(@"Joker"); }

Надеюсь, Apple научит «переключать» несколько новых трюков.


35
Я не могу сказать, действительно ли это противно или действительно круто. Никогда бы не подумал сделать это, спасибо.
конец

2
Хотя мы делаем странные вещи, подобные этой, почему бы не создать свой собственный класс, который упаковывает NSDictionary, заполненный ключами NSString для блочных объектов, а затем предоставляет другой блок для случаев по умолчанию? Вы даже можете иметь поддержку подписки.
ArtOfWarfare

1
Дополнительные очки, если вы создаете подкласс NSDictionary только для этого: P
CommaToast

2
Под капотом именно так C # делает это для больших операторов switch.
Хэнк Шульц

78

Для меня приятный простой способ:

NSString *theString = @"item3";   // The one we want to switch on
NSArray *items = @[@"item1", @"item2", @"item3"];
int item = [items indexOfObject:theString];
switch (item) {
    case 0:
       // Item 1
       break;
    case 1:
       // Item 2
       break;
    case 2:
       // Item 3
       break;
    default:
       break;
}

1
Мне это нравится. Он отвечает потребностям большинства людей, которые ищут ответ на эту проблему, он не требует гораздо большего набора текста, чем аналогичный переключатель в javascript, и он удобочитаем для человека.
EW Parris

4
Я бы не стал сравнивать этот хак с JS switch. Что произойдет, если следующий программист добавит элемент между item1 и item2? Слишком большой потенциал для появления ошибок
Aras

Это хороший хак, поэтому я даю вам большие пальцы за усилия :)
Aras

@Aras Если следующему программисту нужно добавить новую запись, он добавит ее в конец массива с новым оператором case в конце для его обработки. Таким образом, @ "item0" можно добавить после @ "item3" в массиве, а затем добавить регистр 3: для обработки.
сбонкоский

1
Мне полностью нравится твой путь. Это очень аккуратно. Я пишу категорию и должен вернуть UIColor, пока у меня есть строка.
Аликс

11

К сожалению, операторы switch могут использоваться только для примитивных типов. Однако у вас есть несколько вариантов использования коллекций.

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

NSDictionary *stringToNumber = [NSDictionary dictionaryWithObjectsAndKeys:
                                              [NSNumber numberWithInt:6],@"Six",
                                              [NSNumber numberWithInt:7],@"Seven",
                                              [NSNumber numberWithInt:8],@"Eight",
                                              [NSNumber numberWithInt:9],@"Nine",
                                              nil];
NSNumber *number = [stringToNumber objectForKey:cardName];
if(number) [self setValue:[number intValue]];

8

Немного поздно, но для любого в будущем я смог заставить это работать на меня

#define CASE(str) if ([__s__ isEqualToString:(str)])
#define SWITCH(s) for (NSString *__s__ = (s); ; )
#define DEFAULT

Это интересно. Можете ли вы рассказать подробнее?
Чен Ли Ён

6

Вот более разумный способ написать это. Это использовать NSNumberFormatterв «стиле изложения» :

NSString *cardName = ...;

NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setNumberStyle:NSNumberFormatterSpellOutStyle];
NSNumber *n = [nf numberFromString:[cardName lowercaseString]];
[self setValue:[n intValue]];
[nf release];

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


5

Есть и другие способы сделать это, но switchэто не один из них.

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

NSDictionary *cases = @{@"Six" : @6,
                        @"Seven" : @7,
                        //...
                       };

NSNumber *value = [cases objectForKey:cardName];
if (value != nil) {
    [self setValue:[value intValue]];
}

4

НАДЕЖДА .. мой любимый "ObjC Add-On"ObjectMatcher

objswitch(someObject)
    objcase(@"one") { // Nesting works.
        objswitch(@"b")
            objcase(@"a") printf("one/a");
            objcase(@"b") printf("one/b");
            endswitch // Any code can go here, including break/continue/return.
    }
    objcase(@"two") printf("It's TWO.");  // Can omit braces.
    objcase(@"three",     // Can have multiple values in one case.
        nil,              // nil can be a "case" value.
        [self self],      // "Case" values don't have to be constants.
        @"tres", @"trois") { printf("It's a THREE."); }
    defaultcase printf("None of the above."); // Optional default must be at end.
endswitch

И это работает с не-строками, ТОО ... даже в циклах!

for (id ifNumericWhatIsIt in @[@99, @0, @"shnitzel"])
    objswitch(ifNumericWhatIsIt)
        objkind(NSNumber)  printf("It's a NUMBER.... "); 
        objswitch([ifNumericWhatIsIt stringValue])
            objcase(@"3")   printf("It's THREE.\n"); 
            objcase(@"99")  printf("It's NINETY-NINE.\n"); 
            defaultcase     printf("some other Number.\n");
       endswitch
    defaultcase printf("It's something else entirely.\n");
endswitch

It's a NUMBER.... It's NINETY-NINE.
It's a NUMBER.... some other Number.
It's something else entirely.

Лучше всего, так мало {...}«s, :» s и ()«s


3

Objective-c ничем не отличается от c в этом аспекте, он может только включать то, что может c (и preproc def похож на NSInteger, NSUInteger, поскольку в конечном итоге они просто typedef'd для целочисленного типа).

Википедия:

Синтаксис :

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

Интегральные типы :

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


2

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

NSInteger index = [@[@"Six", @"Seven", @"Eight", @"Nine"] indexOfObject:cardName];
if (index != NSNotFound) [self setValue: index + 6];

Обратите внимание, что indexOfObjectпоиск совпадений будет выполняться isEqual:именно так, как в вопросе.


2

Основываясь на идее @Graham Perks, опубликованной ранее, мы разработали простой класс, чтобы сделать переключение между строками довольно простым и понятным.

@interface Switcher : NSObject

+ (void)switchOnString:(NSString *)tString
                 using:(NSDictionary<NSString *, CaseBlock> *)tCases
           withDefault:(CaseBlock)tDefaultBlock;

@end

@implementation Switcher

+ (void)switchOnString:(NSString *)tString
                 using:(NSDictionary<NSString *, CaseBlock> *)tCases
           withDefault:(CaseBlock)tDefaultBlock
{
    CaseBlock blockToExecute = tCases[tString];
    if (blockToExecute) {
        blockToExecute();
    } else {
        tDefaultBlock();
    }
}

@end

Вы бы использовали это так:

[Switcher switchOnString:someString
                   using:@{
                               @"Spades":
                               ^{
                                   NSLog(@"Spades block");
                               },
                               @"Hearts":
                               ^{
                                   NSLog(@"Hearts block");
                               },
                               @"Clubs":
                               ^{
                                   NSLog(@"Clubs block");
                               },
                               @"Diamonds":
                               ^{
                                   NSLog(@"Diamonds block");
                               }
                           } withDefault:
                               ^{
                                   NSLog(@"Default block");
                               }
 ];

Правильный блок будет выполняться в соответствии со строкой.

Суть для этого решения


0

Я не могу прокомментировать ответ cris на @Cris answer, но я хотел бы сказать, что:

Существует ограничение для метода @ cris:

typedef enum не будет принимать буквенно-цифровые значения

typedef enum
{
  12Ace, 23Two, 23Three, 23Four, F22ive ... Jack, Queen, King

} CardType;

Так вот еще один:

Ссылка Stack over flow Перейти к ответу этого пользователя "user1717750"


-1
typedef enum
{
    Six,
    Seven,
    Eight
} cardName;

- (void) switchcardName:(NSString *) param {
    switch([[cases objectForKey:param] intValue]) {
        case Six:
            NSLog(@"Six");
            break;
        case Seven:
            NSLog(@"Seven");
            break;
        case Eight:
            NSLog(@"Eight");
            break;
        default: 
            NSLog(@"Default");
            break;
    }
}

Наслаждайтесь кодированием .....

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