生のInteger
値を持つ列挙型がある場合:
enum City: Int {
case Melbourne = 1, Chelyabinsk, Bursa
}
let city = City.Melbourne
city
値を文字列Melbourne
に変換するにはどうすればよいですか?この種の型名のイントロスペクションは言語で利用できますか?
次のようなもの(このコードは機能しません):
println("Your city is \(city.magicFunction)")
> Your city is Melbourne
Xcode 7ベータ5では、デフォルトでprint(_:)
を使用して型名と列挙型のケースを印刷するか、String
のinit(_:)
初期化子または文字列補間構文を使用してString
に変換できます。あなたの例では:
enum City: Int {
case Melbourne = 1, Chelyabinsk, Bursa
}
let city = City.Melbourne
print(city)
// prints "Melbourne"
let cityName = "\(city)" // or `let cityName = String(city)`
// cityName contains "Melbourne"
そのため、各ケースを切り替えて文字列リテラルを返す便利な関数を定義および保守する必要がなくなりました。さらに、未加工の値のタイプが指定されていない場合でも、これはすべての列挙に対して自動的に機能します。
debugPrint(_:)
およびString(reflecting:)
は、完全修飾名に使用できます。
debugPrint(city)
// prints "App.City.Melbourne" (or similar, depending on the full scope)
let cityDebugName = String(reflecting: city)
// cityDebugName contains "App.City.Melbourne"
これらの各シナリオで印刷されるものをカスタマイズできることに注意してください。
extension City: CustomStringConvertible {
var description: String {
return "City \(rawValue)"
}
}
print(city)
// prints "City 1"
extension City: CustomDebugStringConvertible {
var debugDescription: String {
return "City (rawValue: \(rawValue))"
}
}
debugPrint(city)
// prints "City (rawValue: 1)"
(この「デフォルト」値を呼び出す方法、たとえばswitch文に頼らずに「The city is Melbourne」を印刷する方法が見つかりませんでした。実装での\(self)
の使用description
/debugDescription
を使用すると、無限再帰が発生します。)
上記のString
のinit(_:)
およびinit(reflecting:)
イニシャライザーは、リフレクションされた型が準拠するものに応じて、印刷される内容を正確に説明します。
extension String {
/// Initialize `self` with the textual representation of `instance`.
///
/// * If `T` conforms to `Streamable`, the result is obtained by
/// calling `instance.writeTo(s)` on an empty string s.
/// * Otherwise, if `T` conforms to `CustomStringConvertible`, the
/// result is `instance`'s `description`
/// * Otherwise, if `T` conforms to `CustomDebugStringConvertible`,
/// the result is `instance`'s `debugDescription`
/// * Otherwise, an unspecified result is supplied automatically by
/// the Swift standard library.
///
/// - SeeAlso: `String.init<T>(reflecting: T)`
public init<T>(_ instance: T)
/// Initialize `self` with a detailed textual representation of
/// `subject`, suitable for debugging.
///
/// * If `T` conforms to `CustomDebugStringConvertible`, the result
/// is `subject`'s `debugDescription`.
///
/// * Otherwise, if `T` conforms to `CustomStringConvertible`, the result
/// is `subject`'s `description`.
///
/// * Otherwise, if `T` conforms to `Streamable`, the result is
/// obtained by calling `subject.writeTo(s)` on an empty string s.
///
/// * Otherwise, an unspecified result is supplied automatically by
/// the Swift standard library.
///
/// - SeeAlso: `String.init<T>(T)`
public init<T>(reflecting subject: T)
}
この変更に関する情報については、 リリースノート を参照してください。
現時点では列挙型のケースについては内省していません。それぞれ手動で宣言する必要があります。
enum City : String, Printable {
case Melbourne = "Melbourne"
case Chelyabinsk = "Chelyabinsk"
case Bursa = "Bursa"
var description : String {
get {
return self.rawValue
}
}
}
注:Printable
プロトコルは、現在Playgroundsでは機能しません。プレイグラウンドで文字列を表示したい場合は、手動でtoRaw()を呼び出す必要があります
Raw型をIntにする必要がある場合は、自分で切り替えを行う必要があります。
enum City : Int, Printable {
case Melbourne = 1, Chelyabinsk, Bursa
var description : String {
get {
switch(self) {
case Melbourne:
return "Melbourne"
case Chelyabinsk:
return "Chelyabinsk"
case Bursa:
return "Bursa"
}
}
}
}
Swift-3(Xcode 8.1でテスト済み)では、次のメソッドを列挙に追加できます。
/**
* The name of the enumeration (as written in case).
*/
var name: String {
get { return String(describing: self) }
}
/**
* The full name of the enumeration
* (the name of the enum plus dot plus the name as written in case).
*/
var description: String {
get { return String(reflecting: self) }
}
その後、enumインスタンスの通常のメソッド呼び出しとして使用できます。以前のSwiftバージョンでも動作する可能性がありますが、テストしていません。
あなたの例では:
enum City: Int {
case Melbourne = 1, Chelyabinsk, Bursa
var name: String {
get { return String(describing: self) }
}
var description: String {
get { return String(reflecting: self) }
}
}
let city = City.Melbourne
print(city.name)
// prints "Melbourne"
print(city.description)
// prints "City.Melbourne"
すべての列挙型にこの機能を提供する場合は、拡張機能にすることができます。
/**
* Extend all enums with a simple method to derive their names.
*/
extension RawRepresentable where RawValue: Any {
/**
* The name of the enumeration (as written in case).
*/
var name: String {
get { return String(describing: self) }
}
/**
* The full name of the enumeration
* (the name of the enum plus dot plus the name as written in case).
*/
var description: String {
get { return String(reflecting: self) }
}
}
これはSwift列挙型でのみ機能します。
現在、Objective-Cのenum
sの唯一の方法は、CustomStringConvertible
で列挙型を拡張して、次のようなものにすることです。
extension UIDeviceBatteryState: CustomStringConvertible {
public var description: String {
switch self {
case .Unknown:
return "Unknown"
case .Unplugged:
return "Unplugged"
case .Charging:
return "Charging"
case .Full:
return "Full"
}
}
}
そして、enum
をString
としてキャストします。
String(UIDevice.currentDevice().batteryState)
これはとても残念です。
これらの名前が必要な場合(コンパイラーは正確なスペルを完全に知っているが、アクセスを許可しない-ありがとうSwiftチーム!!-)が、Stringを必要としないか、作成できない場合列挙型のベースである、冗長で扱いにくい選択肢は次のとおりです。
enum ViewType : Int, Printable {
case Title
case Buttons
case View
static let all = [Title, Buttons, View]
static let strings = ["Title", "Buttons", "View"]
func string() -> String {
return ViewType.strings[self.rawValue]
}
var description:String {
get {
return string()
}
}
}
上記を次のように使用できます。
let elementType = ViewType.Title
let column = Column.Collections
let row = 0
println("fetching element \(elementType), column: \(column.string()), row: \(row)")
そして、期待される結果が得られます(同様の列のコードは表示されません)
fetching element Title, column: Collections, row: 0
上記では、description
プロパティがstring
メソッドを参照するようにしましたが、これは好みの問題です。また、いわゆるstatic
変数は、その型を囲む型の名前でスコープ修飾される必要があることに注意してください。コンパイラは記憶が強すぎて、単独でコンテキストをリコールできないためです...
Swiftチームは実際に指揮する必要があります。彼らはenumerate
を使用できない列挙型を作成し、enumerate
を使用できる列挙型は "Sequences"ですが、enum
ではありません!
Swift 2.2の列挙型のString(…)(CustomStringConvertible)のサポートに加えて、いくつかの壊れたリフレクションのサポートもあります。値が関連付けられている列挙型ケースの場合、リフレクションを使用して列挙型ケースのラベルを取得できます。
enum City {
case Melbourne(String)
case Chelyabinsk
case Bursa
var label:String? {
let mirror = Mirror(reflecting: self)
return mirror.children.first?.label
}
}
print(City.Melbourne("Foobar").label) // prints out "Melbourne"
ただし、壊れているということは、「単純な」列挙型の場合、上記のリフレクションベースのlabel
計算プロパティはnil
(boo-hoo)を返すだけであることを意味していました。
print(City.Chelyabinsk.label) // prints out nil
明らかに、Swift 3の後、反射の状況は改善されるはずです。現時点での解決策は、他の回答の1つで提案されているように、String(…)
です。
print(String(City.Chelyabinsk)) // prints out Cheylabinsk
Swiftの場合:
extension UIDeviceBatteryState: CustomStringConvertible {
public var description: String {
switch self {
case .unknown:
return "unknown"
case .unplugged:
return "unplugged"
case .charging:
return "charging"
case .full:
return "full"
}
}
}
変数「batteryState」の場合:
self.batteryState.description
Swiftには、 暗黙的に割り当てられた生の値 と呼ばれるものがあります。基本的に、各ケースに未加工の値を指定せず、enumがString型である場合、ケースの未加工の値自体が文字列形式であると推定されます。試してみてください。
enum City: String {
case Melbourne, Chelyabinsk, Bursa
}
let city = City.Melbourne.rawValue
// city is "Melbourne"
シンプルだが機能する...
enum ViewType : Int {
case Title
case Buttons
case View
}
func printEnumValue(enum: ViewType) {
switch enum {
case .Title: println("ViewType.Title")
case .Buttons: println("ViewType.Buttons")
case .View: println("ViewType.View")
}
}
String(describing:)
イニシャライザを使用して、String以外のrawValuesを持つ列挙型であってもケースラベル名を返すことができます。
enum Numbers: Int {
case one = 1
case two = 2
}
let one = String(describing: Numbers.one) // "one"
let two = String(describing: Numbers.two) // "two"
Enumが@objc
修飾子を使用する場合、これはnot動作することに注意してください。
Objective-Cタイプ用に生成されたSwiftインターフェイスには、@objc
修飾子が含まれない場合があります。それでも、それらのEnumはObjective-Cで定義されているため、上記のようには機能しません。