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/… )
Michael Dautermann

3
มีหลายทางเลือกในการทำ ตัวอย่างเช่นโหลดอาร์เรย์ด้วยค่าและค้นหาการจับคู่ในอาร์เรย์ ไม่มีประสิทธิภาพมากนัก แต่จะลดการทำซ้ำรหัส
Hot Licks

4
ในฐานะที่เป็นข้อความด้านภาษาใหม่ของ Apple (Swift) อนุญาตให้เปรียบเทียบสตริงในข้อความสั่ง switch!
ช่างทำสวน

4
เปลี่ยนเป็น Swift;)
tothemario

คำตอบ:


147

น่าเสียดายที่พวกเขาไม่สามารถ นี่เป็นหนึ่งในสิ่งที่ดีที่สุดและเป็นที่ต้องการมากที่สุดหลังจากการใช้งาน switch statement ดังนั้นหวังว่าพวกเขาจะใช้ bandwagon (ตอนนี้) Java (และอื่น ๆ )!

หากคุณกำลังทำชื่อการ์ดบางทีอาจกำหนดค่าจำนวนเต็มให้กับวัตถุแต่ละการ์ดและเปิดสวิตช์นั้น หรือบางที enum ซึ่งถือเป็นตัวเลขและสามารถเปลี่ยนได้

เช่น

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

} CardType;

ทำอย่างนี้เอซจะเท่ากับกรณี 0 สองเป็นกรณี 1 ฯลฯ


4
@abbood สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ enum ดูการโพสต์NS_ENUM & NS_OPTIONSโดย Mattt Thompson
Basil Bourque

@abbood ความคิดเห็นของคุณควรจะหมายถึงอะไร? ดูเหมือนว่านี่เป็นคำตอบที่ไม่ดี แต่ดูเหมือนว่าฉันจะโอเค คุณช่วยอธิบายได้ไหม
Alan Andrade

ตามที่ฉันเข้าใจแล้ว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
คะแนนพิเศษหากคุณทำ subclass ของ NSDictionary สำหรับสิ่งนี้: P
CommaToast

2
ภายใต้ประทุนนี่คือวิธีที่ C # ทำสำหรับงบสวิตช์ขนาดใหญ่
Hank Schultz

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 จะเกิดอะไรขึ้นถ้าโปรแกรมเมอร์คนต่อไปเพิ่มรายการระหว่าง item1 และ item2 มีโอกาสมากเกินไปสำหรับการแนะนำบั๊ก
Aras

สับดีของมันแม้ว่าดังนั้นฉันให้คุณ thumbs ขึ้นสำหรับความพยายาม :)
Aras

@Aras หากโปรแกรมเมอร์ถัดไปจำเป็นต้องเพิ่มรายการใหม่พวกเขาก็จะเพิ่มมันลงในตอนท้ายของอาร์เรย์ที่มีคำสั่งกรณีใหม่ในตอนท้ายเพื่อจัดการมัน ดังนั้น @ "item0" สามารถเพิ่มหลังจาก @ "item3" ในอาร์เรย์จากนั้นเพิ่มเคส 3: เพื่อจัดการ
sbonkosky

1
ฉันชอบวิธีของคุณโดยสิ้นเชิง มันเรียบร้อยมาก ฉันกำลังเขียนหมวดหมู่และต้องส่งคืน UIColor ในขณะที่ฉันมีสตริงอยู่
Alix

11

น่าเสียดายที่คำสั่งสวิตช์สามารถใช้ได้กับประเภทดั้งเดิมเท่านั้น คุณมีตัวเลือกน้อยในการใช้คอลเลกชัน

อาจเป็นตัวเลือกที่ดีที่สุดที่จะเก็บแต่ละค่าเป็นรายการใน 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

มีวิธีอื่นในการทำเช่นนั้น แต่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.

ดีที่สุดของทั้งหมดมีเพียงไม่กี่ SO {...}'s, :s' และ()'s


3

วัตถุประสงค์ -c ไม่แตกต่างจาก c ในด้านนี้มันสามารถเปลี่ยนสิ่งที่ c สามารถเท่านั้น (และ preproc def เช่น NSInteger, NSUInteger เนื่องจากท้ายที่สุดแล้วพวกเขาจะพิมพ์เป็นประเภทที่สมบูรณ์เท่านั้น)

วิกิพีเดีย:

ไวยากรณ์ c :

สาเหตุที่สวิทช์ควบคุมคำสั่งจะถูกโอนไปยังหนึ่งของงบหลายขึ้นอยู่กับมูลค่าของการแสดงออกซึ่งต้องมีประเภทหนึ่ง

ประเภทหนึ่ง :

ในวิทยาการคอมพิวเตอร์จำนวนเต็มเป็นตัวเลขของข้อมูลชนิดหนึ่งซึ่งเป็นชนิดข้อมูลซึ่งแสดงถึงเซตย่อยบางส่วนของจำนวนเต็มทางคณิตศาสตร์ ชนิดข้อมูลหนึ่งอาจมีขนาดแตกต่างกันและอาจได้รับอนุญาตให้มีค่าลบ


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 ของ:

typedef enum จะไม่ใช้ค่าตัวอักษรและตัวเลข

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

} CardType;

ดังนั้นนี่คืออีกหนึ่ง:

ลิงก์สแต็คโอเวอร์โฟลว์ ไปที่คำตอบผู้ใช้นี้ "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;
    }
}

เพลิดเพลินกับการเข้ารหัส .....

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.