2017-11-01 1 views
2

Ich versuche, ein benutzerdefiniertes Musterabgleich zu schreiben, das es mir ermöglicht, einen Fehler zu aktivieren und mit dem Fehlercode übereinzustimmen. Im folgenden sehen Sie ein Beispiel:Benutzerdefinierte Mustererkennung schlägt fehl mit `Enum case ist kein Mitglied des Typs`

enum ErrorCode: Int { 
    case notSoDumb 
    case dumbError 
} 

let myError = NSError(domain: "My domain", code: ErrorCode.dumbError.rawValue, userInfo: nil) 

func ~=(pattern: ErrorCode, value: NSError) -> Bool { 
    return (ErrorCode(rawValue: value.code) == pattern) 
} 

switch myError { 
case ErrorCode.notSoDumb: 
    print("Not a dumb error") 
case ErrorCode.dumbError: 
    print("Super dumb error") 
default: 
    print("No matches!") 
} 

Der erste Fall in meinem switch-Anweisung einen Fehler von Enum case 'notSoDumb' is not a member of type 'NSError'. Wenn ich die ErrorCode Enum mit ganzen Zahlen ersetzen (und meine benutzerdefinierten ~= Operator aktualisieren Int s und NSError s passen, funktioniert alles einwandfrei

+0

Offensichtlich wird diese Fehlermeldung angezeigt Der Vergleich von "myError", das eine NSError-Instanz mit einem Int ist, sollte zu einem solchen Fehler führen ... Könnten Sie bitte versuchen, zu erklären, was Ihr Hauptzweck ist? –

+1

Dies ist ein bekannter Fehler: https://bugs.swift.org/browse/SR-1121. Zuerst Zuweisen zu einer temporären Arbeit (z. B. "let notSoDumbErrorCode = ErrorCode.notSoDumb", dann 'case notSoDumbErrorCode:'). – Hamish

+0

@Hamish können Sie bitte erklären, was ist das Problem hier? Ich nehme an, dass basierend auf der erwähnten switch-Anweisung ein solcher Fehler generiert werden sollte ... –

Antwort

3

Diese a known bug mit enum Fälle in Mustervergleich ist;. Der Compiler fälschlicherweise davon ausgegangen, dass es immer . enumeration case pattern mit einer Arbeits statt einem expression pattern

bis fixierte, eine Möglichkeit, den Compiler in ‚Expressionsmuster-Modus‘ zu zwingen, besteht darin, zuerst den Fall zu binden ein temporären:

let notSoDumbErrorCode = ErrorCode.notSoDumb 
let dumbErrorCode = ErrorCode.dumbError 

switch myError { 
case notSoDumbErrorCode: 
    print("Not a dumb error") 
case dumbErrorCode: 
    print("Super dumb error") 
default: 
    print("No matches!") 
} 

Allerdings ist das ziemlich klobig. Eine vielleicht besser Abhilfe wäre ein struct mit statischen Mitgliedern zu verwenden, anstatt ein enum:

import Foundation 

struct ErrorCode : Equatable, RawRepresentable { 

    let rawValue: Int 

    static let notSoDumb = ErrorCode(rawValue: 0) 
    static let dumbError = ErrorCode(rawValue: 1) 
} 

let myError = NSError(domain: "My domain", 
         code: ErrorCode.dumbError.rawValue, 
         userInfo: nil) 

func ~=(pattern: ErrorCode, value: NSError) -> Bool { 
    return value.code == pattern.rawValue 
} 

switch myError { 
case ErrorCode.notSoDumb: 
    print("Not a dumb error") 
case ErrorCode.dumbError: 
    print("Super dumb error") 
default: 
    print("No matches!") 
} 

Dies ermöglicht Sie auch zusätzliche Fehlercodes über Erweiterungen hinzufügen später auf der ganzen Linie (es eher wie eine offene Enumeration verhält). Es entfernt zwar die Validierung von init(rawValue:), die möglicherweise wünschenswert ist oder nicht (Sie könnten jedoch immer Ihre eigene init?(rawValue:) implementieren).

Oder as you say in your comment, die Sie mit einem enum halten konnte, sondern eine Zwischenfunktionsaufruf verwenden, wenn Muster des Compilers in ‚Expressionsmuster-Modus‘ zu zwingen, passend zu:

enum ErrorCode : Int { 
    case notSoDumb 
    case dumbError 
} 

// ... 

func identity<T>(_ t: T) -> T { return t } 

switch myError { 
case identity(ErrorCode.notSoDumb): 
    print("Not a dumb error") 
case identity(ErrorCode.dumbError): 
    print("Super dumb error") 
default: 
    print("No matches!") 
} 
Verwandte Themen