Objective-C peut-il activer NSString?


166

Existe-t-il un moyen plus intelligent de réécrire cela?

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
Non, le commutateur ne fonctionne que sur les types int / bool / char / etc.
chown

cette question est un peu similaire à celle postée il y a seulement une heure ( stackoverflow.com/questions/8161319/… )
Michael Dautermann

3
Il existe plusieurs façons de procéder. Par exemple, chargez un tableau avec les valeurs et recherchez une correspondance dans le tableau. Aucun très efficace, mais ils réduisent la duplication de code.
Hot Licks

4
En remarque, le nouveau langage d'Apple (Swift) permet désormais de comparer des chaînes dans une instruction switch!
jaredsmith

4
Passer à Swift;)
tothemario

Réponses:


147

Malheureusement, ils ne peuvent pas. C'est l'une des utilisations les meilleures et les plus recherchées des instructions switch, alors j'espère qu'ils vont dans le train en marche (maintenant) Java (et autres)!

Si vous faites des noms de cartes, attribuez peut-être à chaque objet carte une valeur entière et activez-la. Ou peut-être une énumération, qui est considérée comme un nombre et peut donc être activée.

par exemple

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

} CardType;

Fait de cette façon, Ace serait égal au cas 0, Deux au cas 1, etc.


4
@abbood Pour plus d'informations sur enum, consultez la publication NS_ENUM & NS_OPTIONS de Mattt Thompson.
Basil Bourque

@abbood que signifie votre commentaire? Cela semble être une mauvaise réponse, mais cela me semble correct. Pourriez-vous expliquer ?
Alan Andrade

Si je comprends bien, CardTypene peut être égal à aucun élément inclus, @""par exemple:[CardType isEqualToString:@"Three"]
Adromil Balais

120

Vous pouvez configurer un dictionnaire de blocs, comme ceci:

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

Pour avoir une section «par défaut», remplacez la dernière ligne par:

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

Espérons qu'Apple apprendra à «changer» quelques nouvelles astuces.


35
Je ne peux pas dire si c'est vraiment méchant ou vraiment cool. Je n'aurais jamais pensé à faire ça, merci.
fin

2
Alors que nous faisons des trucs bizarres comme celui-ci, pourquoi ne pas créer votre propre classe qui encapsule un NSDictionary plein de clés NSString pour les objets bloc et fournit ensuite un autre bloc pour les cas par défaut? Vous pouvez même lui faire prendre en charge la notation en indice.
ArtOfWarfare

1
Des points supplémentaires si vous créez une sous-classe NSDictionary juste pour ceci: P
CommaToast

2
Sous le capot, c'est ainsi que C # le fait pour les grandes instructions de commutation.
Hank Schultz

78

Pour moi, un moyen simple et agréable:

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
J'aime ça. Il répond aux besoins de la plupart des personnes à la recherche d'une réponse à ce problème, il ne prend pas beaucoup plus de frappe qu'un commutateur similaire en javascript, et il est lisible par l'homme.
ew parris

4
Je ne comparerais pas ce hack avec le commutateur JS. Que se passe-t-il si le programmeur suivant ajoute un élément entre item1 et item2? Trop de potentiel pour introduire des bugs
Aras

c'est un bon hack cependant, alors je vous donne un coup de pouce pour l'effort :)
Aras

@Aras Si le prochain programmeur a besoin d'ajouter une nouvelle entrée, il l'ajouterait à la fin du tableau avec une nouvelle instruction case à la fin pour la gérer. Ainsi, @ "item0" peut être ajouté après @ "item3" dans le tableau, puis ajoutez un cas 3: pour le gérer.
sbonkosky

1
J'aime totalement ta manière. C'est très soigné. J'écris une catégorie et je dois renvoyer UIColor pendant que j'ai une chaîne avec moi.
Alix

11

Malheureusement, les instructions switch ne peuvent être utilisées que sur les types primitifs. Cependant, vous avez quelques options en utilisant les collections.

La meilleure option serait probablement de stocker chaque valeur en tant qu'entrée dans un 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

Un peu tard mais pour n'importe qui dans le futur, j'ai pu faire en sorte que cela fonctionne pour moi

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

C'est intéressant. Pouvez-vous en dire plus?
Chen Li Yong

6

Voici la manière la plus intelligente d'écrire cela. Il s'agit d'utiliser un NSNumberFormatterdans le "style orthographique" :

NSString *cardName = ...;

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

Notez que le formateur de nombres veut que la chaîne soit en minuscules, nous devons donc le faire nous-mêmes avant de la transmettre au formateur.


5

Il existe d'autres moyens de le faire, mais ce switchn'est pas l'un d'entre eux.

Si vous n'avez que quelques chaînes, comme dans votre exemple, le code que vous avez est correct. Si vous avez de nombreux cas, vous pouvez stocker les chaînes sous forme de clés dans un dictionnaire et rechercher la valeur correspondante:

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

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

4

DE FAR .. mon "Module complémentaire ObjC" FAVORI estObjectMatcher

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

ET ça marche avec des non-cordes, TROP ... en boucles, même!

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.

Mieux encore, il y a SI peu {...}de :, de et ()de


3

Objective-c n'est pas différent de c dans cet aspect, il ne peut activer que ce que c peut (et les définitions de préproc comme NSInteger, NSUInteger, puisqu'elles sont finalement simplement typées en un type intégral).

Wikipédia:

syntaxe c :

L'instruction switch entraîne le transfert du contrôle vers l'une des nombreuses instructions en fonction de la valeur d'une expression, qui doit être de type intégral .

Types intégraux :

En informatique, un entier est une donnée de type de données intégral, un type de données qui représente un sous-ensemble fini des entiers mathématiques. Les types de données intégrales peuvent être de tailles différentes et peuvent ou non être autorisés à contenir des valeurs négatives.


2

Je suis un peu en retard à la fête, mais pour répondre à la question comme indiqué , il existe un moyen plus intelligent:

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

Notez que indexOfObjectrecherchera la correspondance en utilisant isEqual:, exactement comme dans la question.


2

S'appuyant sur l'idée @Graham Perks publiée plus tôt, a conçu une classe simple pour rendre le changement de chaîne assez simple et propre.

@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

Vous l'utiliseriez comme ceci:

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

Le bloc correct s'exécutera selon la chaîne.

Gist pour cette solution


0

Je ne peux pas commenter la réponse de cris sur la réponse @Cris, mais je voudrais dire que:

Il existe une LIMITATION pour la méthode de @ cris:

typedef enum ne prendra pas de valeurs alphanumériques

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

} CardType;

Alors en voici un autre:

Link Stack over flow Aller à la réponse de cet utilisateur "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;
    }
}

Profitez du codage .....

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.