なぜSwiftなのか
C#信者な私ですがiOS/macOSプログラミングをしてみたいのと
プログラミングは目的ではなく手段という心が芽生え始めたから
仕事でも趣味でも今まで主にC#を使ってきてたため主にC#との比較になると思います
まだSwift触り始めて2日なので間違っていたり追記したいものがありましたらお気軽にコメント or 編集リクエストお願いします
環境
・Windows 10 64bit Pro
・IBM Swift Sandbox
未だMac環境を持たないのでブラウザベースで動作を確認しつつやっていきます
そもそもSwiftとは
Apple社が開発したネイティブアプリケーションを開発するための言語
元々はiOSとmacOS向けでだったが2015年にオープンソース化されUbuntu向けにもリリースされた
Windows向けも非公式プロジェクトとして動いている模様
(Bash on Ubuntu on Windowsを使えばUbuntu版Swiftが使えるとか使えないとか)
変数宣言
C#と違って先頭に型ではなくvarが来る
型を明示する場合はvar 変数名:型
と書く
追記 C#にも変数宣言時に型推論を表すvar
キーワードがあるがSwiftでの変数宣言は固定でvar
//型を明示
var message: String = "Hello Swift !"
//型を推論
var message2 = "Hello Swift !"
定数宣言
C#で言うconstほにゃらら
先頭にletとつけて後は変数宣言と同じように
//型を明示
let message: String = "Hello Swift !"
//型を推論
let message2 = "Hello Swift !"
キャスト
C#と違って(型名)値
という形は使えないようです
as
を使いますが、C#のas
に相当する機能はas?
のようです
class GameConsole {}
class XBOXOne : GameConsole {}
class PS4 : GameConsole {}
let xbox: GameConsole = XBOXOne()
let ps4 = PS4()
let downCast = xbox as XBOX //ダウンキャストの為エラー
let upCast = ps4 as GameConsole //アップキャストの為OK
let tryDownCast = xbox as? XBOX //ダウンキャストを試行し成功した場合はOptional型、失敗した場合はnilを返す
let tryCast = xbox as? PS4 //XBOXはPS4にはなれないのでnil(逆もまた然り)
let tryDownCast2 = xbox as! XBOX //強制的にダウンキャストを行う。失敗したときは実行時エラー
プロパティ
C#と同じような機能だがどうやら自動実装プロパティが無いらしい {get;set;}に相当するもの
setアクセサではC#のvalue
の代わりにnewValue
を使用する
追記 : C#には無い機能としてこれからセットされるのを通知するwillSet
とセットされたのを通知するdidSet
が存在する
var _Name: String = ""
var Name: String
{
get
{
return _Name
}
set
{
_Name = newValue
}
}
Name = "ABC"
print(Name) // -> ABC
Name = "DEF"
print(Name) // -> DEF
型チェック
C#と同じでis
を使用する
print(true is Bool) // -> true
print("Hello Swift" is String) // -> true
print("10" is Int) // -> false
Optional型
キャストの方でちらっと出てきたOptional
型
C#ではクラスであれば大方null
を入れることが可能だが、
Swiftではnil
(nullに相当するもの)は入れることが出来ないので
入れれるようにするのがこのOptional
型
(C#のnull許容型int?
のようなもの)
var optional: Optional<String> //この段階でnil
var optional2: String? //こちらでもOK
アンラップ
上記のString?
はString型
とは似て異なるものの為
Stringとして扱うにはOptional型を外す必要がある
方法はいくつかあります
- Forced Unwrapping - 強制的に
var msg: String?
msg = "Hello Swift"
var unwrap_msg: String = msg! // !を使うことで強制的にString?からString型にアンラップ msgの中身がnilだった場合は実行時エラー
print(unwrap_msg) // -> Hello Swift
- Optional Binding - ifと組み合わせて安全に
var msg: String?
msg = "Hello Swift"
if let unwrap = msg //if let構文を使う事でnilの場合はfalse、アンラップ可能な場合はtrueでif文の中身を実行する
{
print(unwrap) // -> Hello Swift
}
guard let unwrap2 = msg else
{
return //guard let ~ else ~ はnilの場合にguardブロックに入る、入った場合はreturnやthrowなどで抜ける必要がある
}
print(unwrap2) //guardを無事抜けれた場合はunwrap2にはアンラップが成功したものが入っていることが保証されるため安全に使用可能
- Optional Chaining - C#でもおなじみ
class GameConsole
{
var Name: String = "Switch"
}
var NSwitch: GameConsole?
NSwitch = GameConsole()
if let console_name: String = NSwitch?.Name //NSwitchがnilでない場合のみ.以降を実行nilの場合はnilを返す
{
print(console_name) // -> Switch
}
- Implicitly Unwrapped Optional - 暗黙的なアンラップ
var msg: String!
msg = "Hello Swift"
var unwrap_msg: String = msg //!はつけなくてもOKだがmsg = nilの場合は実行時エラー
print(unwrap_msg) // -> Hello Swift
null合体演算子
C#のnull合体演算子と全く同じ書き方ができる??
が存在します
let maybeA: Int? = nil
let maybeB: Int? = 0
let a = maybeA ?? 1 // maybeA が nil なので a == 1
let b = maybeB ?? 1 // maybeB が 0 なので b == 0
if文
C#と違ってカッコ()
は不要
逆にカッコを使うときもあるのか…?
if 1 == 1
{
print("true") // -> true
}
範囲演算子
a...b
a..<b
上記の二種類があり、
上はa 以上 b 以下
下はa 以上 b 未満
if文ではif case a...b = c
という風に使います(cがa以上b以下の場合true)
C#にはこれ相当の機能は無いですね…
if case "か"..."ん" = "を"
{
print("[を]は[か]~[ん]の中にあります")
// -> [を]は[か]~[ん]の中にあります
}
@lovee さん提供
加えてif文ではif case (a...b).contains(c)
という書き方もできます
(if文に於いてはこちらが主流なようです!)
関数
func 関数名(引数) -> 戻り値の型
という形
-> って最近C++11か14辺りで似たようなの無かったっけ
引数は引数名: 型名
と書き、呼び出し側では明示的にfunc(引数名: 引数の値)
と書く必要がある
明示的に書くのが面倒な場合は関数宣言時に_ 引数名: 型名
と_(アンダースコア)
を付けると呼び出し側で明示的に指定する必要が無くなる
例)func("hogehoge")
//引数・戻り値無し
func hogeFunc()
{
}
//引数有(呼び出し側で引数を明示的に指定する必要がある)
func hogeFunc2(name: String)
{
print(name)
}
//引数有(呼び出し側で引数を明示的に指定する必要はない)
func hogeFunc3(_ name: String)
{
print(name)
}
//戻り値あり
func hogeFunc4() -> String
{
return "Hello hogeFunc4 !"
}
//デフォルト引数
func hogeFunc5(_ name: String = "hogeFunc5")
{
print(name)
}
hogeFunc()
hogeFunc2(name: "Hello hogeFunc2 !") // -> Hello hogeFunc2 !
hogeFunc3("Hello hogeFunc3 !") // -> Hello hogeFunc3 !
print(hogeFunc4()) // -> Hello hogeFunc4 !
hogeFunc5() // -> Hello hogeFunc5 !
参照渡し
C#と同様classは全てデフォルトで参照渡し、
structは全てデフォルトで値渡し(コピー)だが
値型を参照で渡したい、参照型を参照渡し(参照先の入れ替え)をしたいという場合は inout を使用すれば可能
渡す側の変数には頭に&
を付ける
func Assign(_ numeric: Int,_ variable: inout Int)
{
variable = numeric
}
var result: Int = 0
Assign(100, &result)
print(result) // -> 100
アクセス修飾子
C#ではpublic
protected internal
protected
internal
private
の5種類で明示的に指定しなかった場合はprivateだが
Swiftでは以下の通り
アクセス修飾子 | 説明 |
---|---|
open | どこからでもアクセス可能 |
public | アクセスはどこからでも可能だがopenと違い別モジュールからの継承とオーバーライドが不可 |
internal | モジュール内からのみアクセス可能 |
fileprivate | 同じソースファイル内からのみアクセス可能 |
private | 定義されているスコープの範囲に限りアクセス可能 |
クラス
C#と同じ
class GameConsole {}
継承
C#同様。(多重継承も不可)
親クラスには super
でアクセス可能
class GameConsole {}
class DreamCast : GameConsole {}
オーバーライド
func
の前にoverride
とつけるだけでOK
class GameConsole
{
func Execute() { /* 実行処理 */}
}
class DreamCast : GameConsole
{
override func Execute()
{
super.Execute();
}
}
構造体
C#と同じで構造体とクラスの違いは値型か参照型の違い
書き方もC#と同様
struct Gamesoft {}
列挙型
enum { case 列挙名 = 列挙するデータ }
と宣言する
またC#等と違い関数を定義することが可能
型名を省略した際はrawValue等で中身が取り出せないため、if,switch等での条件分岐用…?
enum Encode : String //Stringと型を明示的に指定することでStringも使用可能
{
case UTF8 = "UTF-8"
case ShiftJIS = "Shift-JIS"
case EUC = "EUC-JP"
}
enum Numeric : Int //Intと使用しない場合はrawValueで中身を取り出せない(指定しない場合は中身がそもそも存在しない??)
{
case Zero, One, Two, Three
}
print(Encode.UTF8.rawValue) // -> UTF-8
print(Numeric.Zero.rawValue) // -> 0
var jp_default_encode: Encode = .ShiftJIS // 型を明示した場合 .列挙名 という風に書くことが可能
print(jp_default_encode.rawValue) // -> Shift-JIS
Protocol (インターフェース)
C#やJavaでいうところのinterface
書き方はさほど変わらない。C#と同じでprotocolは同じprotocolであれば複数継承することが可能
protocol UserType {}
拡張メソッド
C#お馴染みの拡張メソッド
Swiftにもあって一安心(拡張メソッド依存者)
宣言方法は exntension [extension対象クラス] {}
extension String
{
func ToInt() -> Int
{
let result = Int(self)
return result!
}
}
print("10".ToInt()) // -> 10
文字列フォーマット
C#では6.0以前では文字列のフォーマットに string.Format()
を
C#6.0以降からは$"{}"
が使われてて$"{}"
こいつが凄い便利だが、Swiftにも似たようなものがあり、下記のように書く
"\(変数)"
let name: String = "山田"
let age: Int = 18
print("\(name)は\(age)歳だ") // -> 山田は18歳だ
エイリアス
別名を付ける
C#で言うusing Reader = System.IO.StreamReader
みたいな物
typealias Name = String
let name: Name = "AziO"
コレクション
配列 (Array)
C#で言うstring[] str
のようなもの
append
関数を使用してデータを追加する
var numberArray = [1, 2, 3, 4, 5]
numberArray[2] *= -1
for i in numberArray
{
print(i) // 1, 2, -3, 4, 5
}
var emptyArray: [String] = []
emptyArray.append("Hello")
emptyArray.append("Swift")
for i in emptyArray
{
print(i) // Hello , Swift
}
辞書 (Dictionary)
C#で言うDictionary<T, T2>
let test = ["山田太郎": 60, "山田花子": 75, "鈴木一郎": 30]
for i in test
{
print("\(i.key) = \(i.value)")
// ->
/*
山田太郎 = 60
鈴木一郎 = 30
山田花子 = 75
*/
}
var emptyDictionary: [String: Int] = [:]
emptyDictionary["出木杉英才"] = 100
for i in emptyDictionary
{
print("\(i.key) = \(i.value)") // -> 出木杉英才 = 100
}
タプル (Tuple)
C#でも昔からあったが使いにくく、C#7でSwiftと似たような構文が使えるようになったやつ
let jp_country = ("Japan", "円", "東京")
let jp_country2 = (name: "Japan", currency: "円", capital: "東京")
print(jp_country.0) // -> Japan
print(jp_country2.currency) // -> 円
print(jp_country.2) // -> 東京
func ReturnCountry(_ Name: String, _ Currency: String, _ Capital: String)
-> (name: String, currency: String, capital: String)
{
return (Name, Currency, Capital)
}
print(ReturnCountry("USA", "$", "Washington, D.C.")) // -> (name: "USA", currency: "$", capital: "Washington, D.C.")
ループ
forループ
現行バージョンSwift3系ではC言語などであるfor var i = 0; i < 100; i++
といった構文が使用できなった…らしい(?)
代わりにfor-in Loop形式で対応しましょう
for i in 1...10
{
print(i) // -> 1 ~ 10
}
let loop_cnt = 5
for i in 1...loop_cnt
{
print(i) // -> 1 ~ 5
}
for i in 1..<loop_cnt
{
print(i) // -> 1 ~ 4
}
while ループ
C#で言うwhileはほぼ同じ書き方while 条件式 { ~ }
do-whileはrepeat { ~ } while 条件式
と書く
var i = 0
while case 0...10 = i
{
print(i) // -> 0 ~ 10
i += 1
}
foreachは?
C#で言うforeachはfor-in Loopに組み込まれているのでそちらを使いましょう
C#のList
に実装されているForEach(Action)
に似たものならあります
Array(0...100).forEach {print("\($0)")} // -> 0 ~ 100 $0はクロージャ引数で $0,1,2,3と順に振られていくらしい
LINQは???
C#と言えばLINQというイメージ、むしろLINQがあったからC#を使ってるといっても過言ではない私ですが
LINQっぽい物はあるようです
下のサンプルはどんな仕組みで動いてるのかさっぱりなので正しいコードかどうかはかなり微妙です(動作はします)
//テストの結果を用意し50点以上を取った人の名前を取得
let test = ["山田太郎": 60, "山田花子": 75, "鈴木一郎": 30]
let score = test.filter { $0.value >= 50}.map { $0.key }
for i in score
{
print(i) // -> 山田太郎 , 山田花子
}
let average = Double( test.map{ $0.value}.reduce(0){ $0 + $1 } ) / Double(test.count)
print(average) // -> 55.0
まとめた感想
「あれ?Swift割と良さげじゃね…?」
C#にどっぷりつかっていた身としては気になる点はいくつかありますがおおむね高評価です
気になる点リスト(こんなんあるよ、それはちがうよあればお気軽にコメント等どうぞ)
- LINQっぽい機能(なんていう正式名称なんでしょうかあれ…)がLINQに比べて貧弱な気がする
- @loveeさん提供 : flatMapを使用すればLINQでできることは大体できる模様
- プロパティの自動実装
string Name {get; set;}
みたいなの無いとちょっと不便- C#/Java程プロパティは乱用しないようです
- C#と比べてパフォーマンスはどれぐらい出るんだろう?(実機が無いので検証が出来ない)
- Objective-C時代は参照カウンタに悩まされた経験があるけどSwiftはGCとかそこらへんどうなってるんだろう
- @takabosoft さん提供 : 現在はARCと言ったスマートポインタのようなものがあるため昔ほど気にならなくなった
もっといろいろ判明したら Part2 とか上げてみたいと思います
参考にしたサイト様
とても参考になりました、ありがとうございます