LoginSignup
21
20

More than 5 years have passed since last update.

[Swift]WinでやるC#erの為のSwift基本文法覚書

Last updated at Posted at 2017-06-27

なぜ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.swift
//型を明示
var message: String = "Hello Swift !"

//型を推論
var message2 = "Hello Swift !"

定数宣言

C#で言うconstほにゃらら
先頭にletとつけて後は変数宣言と同じように

let.swift
//型を明示
let message: String = "Hello Swift !"

//型を推論
let message2 = "Hello Swift !"

キャスト

C#と違って(型名)値という形は使えないようです
asを使いますが、C#のasに相当する機能はas?のようです

cast.swift
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が存在する

property.swift
var _Name: String = ""
var Name: String
{
    get
    {
        return _Name
    }
    set
    {
        _Name = newValue
    }
}

Name = "ABC"
print(Name) // -> ABC

Name = "DEF"
print(Name) // -> DEF

型チェック

C#と同じでisを使用する

check.swift
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?のようなもの)

optional.swift
var optional: Optional<String> //この段階でnil
var optional2: String? //こちらでもOK

アンラップ

上記のString?String型とは似て異なるものの為
Stringとして扱うにはOptional型を外す必要がある
方法はいくつかあります

  • Forced Unwrapping - 強制的に
force_unwrap.swift
var msg: String?
msg = "Hello Swift"
var unwrap_msg: String = msg! // !を使うことで強制的にString?からString型にアンラップ msgの中身がnilだった場合は実行時エラー
print(unwrap_msg) // -> Hello Swift
  • Optional Binding - ifと組み合わせて安全に
optional_unwrap.swift
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#でもおなじみ
chaining_unwrap.swift
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 - 暗黙的なアンラップ
implicit_unwrap.swift
var msg: String!
msg = "Hello Swift"
var unwrap_msg: String = msg //!はつけなくてもOKだがmsg = nilの場合は実行時エラー
print(unwrap_msg) // -> Hello Swift

null合体演算子

C#のnull合体演算子と全く同じ書き方ができる??が存在します

null.swift
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.swift
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#にはこれ相当の機能は無いですね…

range.swift
if case "か"..."ん" = "を"
{
    print("[を]は[か]~[ん]の中にあります")
    // -> [を]は[か]~[ん]の中にあります
}

@lovee さん提供
加えてif文ではif case (a...b).contains(c)という書き方もできます
(if文に於いてはこちらが主流なようです!)

関数

func 関数名(引数) -> 戻り値の型という形
-> って最近C++11か14辺りで似たようなの無かったっけ
引数は引数名: 型名と書き、呼び出し側では明示的にfunc(引数名: 引数の値)と書く必要がある
明示的に書くのが面倒な場合は関数宣言時に_ 引数名: 型名_(アンダースコア)を付けると呼び出し側で明示的に指定する必要が無くなる
例)func("hogehoge")

func.swift
//引数・戻り値無し
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 を使用すれば可能
渡す側の変数には頭に&を付ける

inout.swift
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.swift
class GameConsole {}

継承

C#同様。(多重継承も不可)
親クラスには super でアクセス可能

subclass.swift
class GameConsole {}
class DreamCast : GameConsole {}

オーバーライド

funcの前にoverrideとつけるだけでOK

override.swift
class GameConsole 
{
    func Execute() { /* 実行処理 */}
}
class DreamCast : GameConsole
{
    override func Execute()
    {
        super.Execute();
    }
}

構造体

C#と同じで構造体とクラスの違いは値型か参照型の違い
書き方もC#と同様

struct.swift
struct Gamesoft {}

列挙型

enum { case 列挙名 = 列挙するデータ }と宣言する
またC#等と違い関数を定義することが可能
型名を省略した際はrawValue等で中身が取り出せないため、if,switch等での条件分岐用…?

enum.swift
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.swift
protocol UserType {}

拡張メソッド

C#お馴染みの拡張メソッド
Swiftにもあって一安心(拡張メソッド依存者)

宣言方法は exntension [extension対象クラス] {}

extension.swift
extension String
{
    func ToInt() -> Int
    {
        let result = Int(self)
        return result!
    }
}
print("10".ToInt()) // -> 10

文字列フォーマット

C#では6.0以前では文字列のフォーマットに string.Format()
C#6.0以降からは$"{}"が使われてて$"{}"こいつが凄い便利だが、Swiftにも似たようなものがあり、下記のように書く
"\(変数)"

format.swift
let name: String = "山田"
let age: Int = 18

print("\(name)\(age)歳だ") // -> 山田は18歳だ

エイリアス

別名を付ける
C#で言うusing Reader = System.IO.StreamReaderみたいな物

alias.swift
typealias Name = String
let name: Name = "AziO"

コレクション

配列 (Array)

C#で言うstring[] strのようなもの
append関数を使用してデータを追加する

array.swift
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>

dictionary.swift
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と似たような構文が使えるようになったやつ

tuple.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.swift
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 条件式と書く

while.swift
var i = 0
while case 0...10 = i
{
    print(i) // -> 0 ~ 10
    i += 1
}

foreachは?

C#で言うforeachはfor-in Loopに組み込まれているのでそちらを使いましょう
C#のListに実装されているForEach(Action)に似たものならあります

foreach.swift
Array(0...100).forEach {print("\($0)")} // -> 0 ~ 100 $0はクロージャ引数で $0,1,2,3と順に振られていくらしい

LINQは???

C#と言えばLINQというイメージ、むしろLINQがあったからC#を使ってるといっても過言ではない私ですが
LINQっぽい物はあるようです
下のサンプルはどんな仕組みで動いてるのかさっぱりなので正しいコードかどうかはかなり微妙です(動作はします)

LINQ.swift
//テストの結果を用意し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 とか上げてみたいと思います

参考にしたサイト様

とても参考になりました、ありがとうございます

21
20
14

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
20