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

この質問は時間前に投稿この1(に似ていstackoverflow.com/questions/8161319/...
マイケル・Dautermann

3
それを行うにはいくつかの代替方法があります。たとえば、値を含む配列をロードし、配列内の一致を検索します。それほど効率的なものはありませんが、コードの重複を減らします。
Hot Licks

4
補足として、Appleの新しい言語(Swift)では、switchステートメントで文字列を比較できるようになりました。
jaredsmith 14年

4
Swiftに切り替え;)
tothemario 2016年

回答:


147

残念ながらできません。これは、switchステートメントの使用率が最も高く、最も求められているものの1つであるため、(現在の)Java(およびその他の)バンドワゴンに期待しています!

カード名を使用している場合は、各カードオブジェクトに整数値を割り当てて、それをオンにします。または、おそらく列挙型であり、これは数値と見なされるため、オンに切り替えることができます。

例えば

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

} CardType;

このように実行すると、エースはケース0に等しくなり、2はケース1に等しくなります。


4
@abbood enumの詳細については、Mattt ThompsonによるNS_ENUM&NS_OPTIONSの投稿を参照してください。
バジルブルク2013年

@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"); }

うまくいけば、アップルはいくつかの新しいトリックを「切り替える」ことを教えてくれるでしょう。


35
これが本当に厄介なのか、本当にクールなのかはわかりません。これを行うことを考えたことはなかったでしょう、ありがとう。
ENDY

2
このような奇妙なことをしている間に、ブロックオブジェクトのNSStringキーでいっぱいのNSDictionaryをラップし、デフォルトのケースに別のブロックを提供する独自のクラスを作ってみませんか?下付き表記をサポートすることもできます。
ArtOfWarfare 2013年

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 2014年

4
私はこのハックをJSスイッチと比較しません。次のプログラマーがitem1とitem2の間にアイテムを追加するとどうなりますか?バグを導入する可能性が高すぎる
Aras

でもそれは素晴らしいハックなので、私はあなたにその努力を高く評価します:)
Aras

@Aras次のプログラマが新しいエントリを追加する必要がある場合、それを処理するために最後に新しいcaseステートメントを付けて、配列の最後に追加します。そのため、配列の@ "item3"の後に@ "item0"を追加してから、ケース3:を追加して処理できます。
sbonkosky 2014年

1
私は完全にあなたのやり方が好きです。それはとてもきちんとしています。私はカテゴリを書いていて、文字列を持っているときにUIColorを返す必要があります。
Alix、2014

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

これは面白い。さらに詳しく説明していただけますか?
Chen Li Yong、

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

これを行う方法は他にもありますが、それらの方法のswitch1つではありません。

あなたの例のようにいくつかの文字列しかない場合、あなたが持っているコードは問題ありません。多くのケースがある場合は、文字列をキーとして辞書に格納し、対応する値を検索できます。

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

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

4

FAR ..で私のお気に入り「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

そしてそれは文字列ではなく、TOO ...ループでも動作します!

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.

すべてのベストは、SOほとんどない{...}の、:の、と()


3

Objective-cは、この点でcと同じです。cができること(そして、最終的には整数型にtypedefされているだけなので、NSIntegerやNSUIntegerのようなpreproc defのみ)を切り替えることができます。

ウィキペディア:

c構文

switchステートメントを使用すると、式の値に応じて、制御がいくつかのステートメントの1つに転送されます。式は整数型なければなりません

積分型

コンピュータサイエンスでは、整数は整数データ型のデータムであり、数学的整数の有限のサブセットを表すデータ型です。整数データ型はサイズが異なる場合があり、負の値を含めることが許可されている場合と許可されていない場合があります。


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の回答にコメントすることはできませんが、私はそれを言いたいです。

@crisの方法には制限があります:

typedef enumは英数字の値を取りません

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

} CardType;

だからここに別のものがあります:

フロー上のリンクスタック このユーザーの回答に行く「user1717570」


-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;
    }
}

コーディングを楽しむ.....

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.