【问题标题】:Can Objective-C switch on NSString?Objective-C 可以打开 NSString 吗?
【发布时间】:2011-12-30 23:54:27
【问题描述】:

有没有更聪明的方法来重写这个?

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

【问题讨论】:

  • 不,开关仅适用于 int/bool/char/etc 类型。
  • 这个问题和一个小时前发布的这个问题有点相似(stackoverflow.com/questions/8161319/…
  • 有几种替代方法可以做到这一点。例如,加载一个包含值的数组并在数组中搜索匹配项。没有一个非常有效,但它们确实减少了代码重复。
  • 附带说明一下,Apple 的新语言 (Swift) 现在允许在 switch 语句中比较字符串!
  • 切换到 Swift ;)

标签: objective-c switch-statement


【解决方案1】:

您可以使用宏方法来实现它:

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

SWITCH (string) {
    CASE (@"TestString") {
        break;
    }
    CASE (@"YetAnotherString") {
        break;
    }
    CASE (@"Test") {
        break;
    }
    DEFAULT {
        break;
    }
 }

【讨论】:

    【解决方案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");
                                   }
     ];
    

    正确的块会根据字符串执行。

    Gist for this solution

    【讨论】:

      【解决方案3】:
      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;
          }
      }
      

      享受编码.....

      【讨论】:

        【解决方案4】:

        有点晚了,但对于未来的任何人来说,我都能让它为我工作

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

        【讨论】:

        • 这很有趣。你能详细说明一下吗?
        【解决方案5】:

        您可以设置一个块字典,如下所示:

        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 会教 'switch' 一些新技巧。

        【讨论】:

        • 我不知道这是真的讨厌还是真的很酷。从来没有想过这样做,谢谢。
        • 虽然我们正在做这样奇怪的事情,为什么不创建自己的类,它为块对象包装一个充满 NSString 键的 NSDictionary,然后为默认情况提供另一个块?你甚至可以让它支持下标符号。
        • 如果您为此创建一个 NSDictionary 子类,则可以加分:P
        • 在底层,这就是 C# 对大型 switch 语句的处理方式。
        • 这是幻想
        【解决方案6】:

        我参加聚会有点晚了,但要按所述回答问题,有一个更聪明的方法:

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

        请注意,indexOfObject 将使用isEqual: 查找匹配项,与问题中的完全相同。

        【讨论】:

          【解决方案7】:

          到目前为止..my FAVORITE "ObjC Add-On" is 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.
          

          最重要的是,{...}:() 的数量很少

          【讨论】:

            【解决方案8】:

            还有其他方法可以做到这一点,但switch 不是其中之一。

            如果您只有几个字符串,例如您的示例,那么您拥有的代码就可以了。如果您有很多情况,您可以将字符串作为键存储在字典中并查找相应的值:

            NSDictionary *cases = @{@"Six" : @6,
                                    @"Seven" : @7,
                                    //...
                                   };
            
            NSNumber *value = [cases objectForKey:cardName];
            if (value != nil) {
                [self setValue:[value intValue]];
            }
            

            【讨论】:

              【解决方案9】:

              我无法评论 cris 对@Cris 的回答,但我想说:

              @cris 的方法有一个限制:

              typedef 枚举不会采用字母数字值

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

              所以这是另一个:

              Link Stack over flow去这个用户回答“user1717750”

              【讨论】:

                【解决方案10】:

                对我来说,一个不错的简单方法:

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

                【讨论】:

                • 我喜欢这个。它满足了大多数人寻找这个问题的答案的需求,它不需要比 JavaScript 中的类似开关更多的输入,而且它是人类可读的。
                • 我不会将这个 hack 与 JS 开关进行比较。如果下一个程序员在 item1 和 item2 之间添加一个项目会发生什么?引入错误的可能性太大
                • 它是一个不错的 hack,所以我为你的努力竖起大拇指 :)
                • @Aras 如果下一个程序员需要添加一个新条目,那么他们会将它添加到数组的末尾,并在末尾添加一个新的 case 语句来处理它。所以可以在数组中@"item3"后面加上@"item0",然后加一个case 3:来处理。
                • 我非常喜欢你的方式。它非常整洁。我正在编写类别,需要在我有字符串时返回 UIColor。
                【解决方案11】:

                这是更聪明的写法。在"spell-out style" 中使用NSNumberFormatter

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

                请注意,数字格式化程序希望字符串小写,因此我们必须在将其传递给格式化程序之前自己这样做。

                【讨论】:

                  【解决方案12】:

                  Objective-c 在这方面与 c 没有什么不同,它只能切换 c 能做什么(以及 preproc def 之类的 NSInteger、NSUInteger,因为它们最终只是 typedef 为整数类型)。

                  维基百科:

                  c syntax:

                  switch 语句根据表达式的值将控制权转移到多个语句之一,表达式的值必须是整数类型

                  Integral Types:

                  在计算机科学中,整数是整数数据类型的数据,一个 表示数学的某个有限子集的数据类型 整数。整数数据类型可能具有不同的大小,并且可能或可能 不允许包含负值。

                  【讨论】:

                    【解决方案13】:

                    不幸的是,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]];
                    

                    【讨论】:

                      【解决方案14】:

                      不幸的是,他们不能。这是 switch 语句的最佳和最受追捧的用法之一,因此希望它们能够加入(现在)Java(和其他)潮流!

                      如果您正在使用卡片名称,也许可以为每个卡片对象分配一个整数值并打开它。或者可能是一个枚举,它被视为一个数字,因此可以打开。

                      例如

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

                      这样做,Ace 等于 case 0,2 等于 case 1,等等。

                      【讨论】:

                      • @abbood 有关枚举的更多信息,请参阅 Mattt Thompson 的帖子NS_ENUM & NS_OPTIONS
                      • @abbood 你的评论应该是什么意思?听起来这是一个糟糕的答案,但对我来说似乎还可以。你能解释一下吗?
                      • 据我了解,CardType 不能等于任何封闭的@"" 例如:[CardType isEqualToString:@"Three"]
                      猜你喜欢
                      • 2011-03-12
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-12-28
                      • 1970-01-01
                      • 2011-05-01
                      • 2015-02-22
                      相关资源
                      最近更新 更多