Czy Objective-C może włączyć NSString?

166

Czy istnieje bardziej inteligentny sposób na przepisanie tego?

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];
} 
James Raitsev
źródło
1
Nie, przełącznik działa tylko na typach int / bool / char / etc.
chown
to pytanie jest trochę podobne do tego, które zostało wysłane zaledwie godzinę temu ( stackoverflow.com/questions/8161319/… )
Michael Dautermann
3
Jest na to kilka alternatywnych sposobów. Np. Załaduj tablicę z wartościami i wyszukaj dopasowanie w tablicy. Żadne z nich nie są zbyt wydajne, ale redukują powielanie kodu.
Hot Licks
4
Na marginesie, nowy język Apple (Swift) umożliwia teraz porównywanie ciągów znaków w instrukcji przełącznika!
jaredsmith
4
Przełącz na Swift;)
tothemario

Odpowiedzi:

147

Niestety nie mogą. Jest to jedno z najlepszych i najbardziej poszukiwanych zastosowań instrukcji przełącznika, więc miejmy nadzieję, że wskoczą na (obecnie) Java (i inne) modę!

Jeśli tworzysz nazwy kart, przypisz każdemu obiektowi karty wartość całkowitą i włącz ją. A może wyliczenie, które jest traktowane jako liczba i dlatego można je włączyć.

na przykład

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

} CardType;

W ten sposób as byłby równy przypadkowi 0, dwa przypadkowi 1 itd.

Chris
źródło
4
@abbood Aby uzyskać więcej informacji na temat wyliczenia, zobacz artykuł NS_ENUM i NS_OPTIONS autorstwa Mattta Thompsona.
Basil Bourque
@abbood, co ma oznaczać Twój komentarz? Wygląda na to, że to zła odpowiedź, ale wydaje mi się w porządku. Czy możesz wytłumaczyć ?
Alan Andrade
Jak rozumiem, CardTypenie może równać się żadnemu załączonemu @""np .:[CardType isEqualToString:@"Three"]
Adromil Balais
120

Możesz skonfigurować słownik bloków, na przykład:

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

Aby mieć sekcję „domyślną”, zamień ostatni wiersz na:

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

Miejmy nadzieję, że Apple nauczy „zamiany” kilku nowych sztuczek.

Graham Perks
źródło
35
Nie mogę powiedzieć, czy to jest naprawdę paskudne, czy naprawdę fajne. Nigdy bym tego nie pomyślał, dzięki.
koniec
2
Chociaż robimy takie dziwne rzeczy, dlaczego nie stworzyć własnej klasy, która opakowuje NSDictionary pełen kluczy NSString dla obiektów blokowych, a następnie zapewnia kolejny blok dla przypadków domyślnych? Możesz nawet mieć obsługę notacji z indeksem dolnym.
ArtOfWarfare
1
Dodatkowe punkty, jeśli utworzysz podklasę NSDictionary tylko w tym celu: P
CommaToast
2
Pod maską to sposób, w jaki C # robi to w przypadku dużych instrukcji przełącznika.
Hank Schultz
78

Dla mnie miły, łatwy sposób:

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;
}
sbonkosky
źródło
1
Lubię to. Odpowiada na potrzeby większości poszukujących odpowiedzi na ten problem, nie wymaga dużo więcej pisania niż podobny przełącznik w javascript i jest czytelny dla człowieka.
ew parris
4
Nie porównałbym tego hacka z przełącznikiem JS. Co się stanie, jeśli następny programista doda element między item1 i item2? Zbyt duży potencjał do wprowadzenia błędów
Aras
to niezły hack, więc daję ci kciuki za wysiłek :)
Aras
@Aras Jeśli następny programista będzie potrzebował dodać nowy wpis, doda go na końcu tablicy z nową instrukcją case na końcu, aby go obsłużyć. Tak więc @ "item0" można dodać po @ "item3" w tablicy, a następnie dodać przypadek 3: aby go obsłużyć.
sbonkosky
1
Całkowicie lubię twój sposób. To bardzo zadbane. Piszę kategorię i muszę zwrócić UIColor, gdy mam ze sobą ciąg.
Alix
11

Niestety instrukcji switch można używać tylko w przypadku typów pierwotnych. Masz jednak kilka opcji korzystania z kolekcji.

Prawdopodobnie najlepszą opcją byłoby przechowywanie każdej wartości jako pozycji w 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]];
ughoavgfhw
źródło
8

Trochę późno, ale dla każdego w przyszłości udało mi się to zrobić dla mnie

#define CASE(str) if ([__s__ isEqualToString:(str)])
#define SWITCH(s) for (NSString *__s__ = (s); ; )
#define DEFAULT
Newyork167
źródło
To jest interesujące. Czy możesz rozwinąć więcej?
Chen Li Yong
6

Oto bardziej inteligentny sposób, aby to napisać. Używa się znaku NSNumberFormatterw stylu „spell-out” :

NSString *cardName = ...;

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

Zwróć uwagę, że program formatujący liczby chce, aby ciąg był zamieniany na małe litery, więc musimy to zrobić sami przed przekazaniem go do programu formatującego.

Dave DeLong
źródło
5

Są na to inne sposoby, ale switchnie jest jednym z nich.

Jeśli masz tylko kilka ciągów, jak w przykładzie, kod, który masz, jest w porządku. Jeśli masz wiele przypadków, możesz przechowywać ciągi jako klucze w słowniku i wyszukać odpowiednią wartość:

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

NSNumber *value = [cases objectForKey:cardName];
if (value != nil) {
    [self setValue:[value intValue]];
}
Caleb
źródło
4

DALEKO .. moim ULUBIONYM "Dodatkiem ObjC" jestObjectMatcher

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

I działa z elementami niebędącymi łańcuchami, ZBYT ... nawet w pętlach!

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.

Najlepsze jest to, że tak mało {...}„s, :” s, a ()„s

Alex Gray
źródło
3

Objective-c nie różni się od c w tym aspekcie, może tylko włączać to, co c może (i preproc def jest jak NSInteger, NSUInteger, ponieważ ostatecznie są one po prostu zdefiniowane jako typ całkowity).

Wikipedia:

c składnia :

Instrukcja switch powoduje przekazanie kontroli do jednej z kilku instrukcji w zależności od wartości wyrażenia, które musi mieć typ całkowity .

Typy całkowe :

W informatyce liczba całkowita jest datum integralnego typu danych, typu danych, który reprezentuje pewien skończony podzbiór matematycznych liczb całkowitych. Całkowe typy danych mogą mieć różne rozmiary i mogą, ale nie muszą, zawierać wartości ujemne.

chown
źródło
2

Trochę spóźniłem się na imprezę, ale aby odpowiedzieć na powyższe pytanie , jest bardziej inteligentny sposób:

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

Zauważ, że indexOfObjectbędzie szukać dopasowania używając isEqual:, dokładnie tak, jak w pytaniu.

ilya n.
źródło
2

Opierając się na opublikowanym wcześniej pomyśle @Graham Perks, zaprojektowano prostą klasę, dzięki której przełączanie łańcuchów jest dość proste i przejrzyste.

@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

Używałbyś tego w ten sposób:

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

Odpowiedni blok zostanie wykonany zgodnie z łańcuchem.

Streszczenie tego rozwiązania

Chuck Krutsinger
źródło
0

Nie mogę skomentować odpowiedzi Crisa w odpowiedzi @Cris, ale chciałbym powiedzieć, że:

Metoda @ cris ma OGRANICZENIE:

typedef enum nie przyjmuje wartości alfanumerycznych

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

} CardType;

Więc oto kolejny:

Link Stack over flow Przejdź do odpowiedzi użytkownika „user1717750”

Puru
źródło
-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;
    }
}

Miłego kodowania .....

Ek SAD.
źródło