質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

1447閲覧

Swift: 構造体同士のキャスト

garubor01

総合スコア1

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2021/09/03 02:27

前提・実現したいこと

  • 以下構造体AとBの相互キャスト

構造体A

// 構造体A struct A { init() {} var a : Int? var b : Int? } // 構造体B struct B { init() {} var a : Int? var b : Int? var c : Int? }

最終的に↓のような動きができると嬉しい。

Swift

11. 2 1. 3 var a = A() 4 a.a = 1 5 a.b = 2 6 7 var b = ~~AをBにキャストしたもの~~ 8 9 b.a → 1 10 b.b → 2 11 b.c → nil 12 13 2. 14 var b = B() 15 b.a = 1 16 b.b = 2 17 b.c = 3 18 19 var a = ~~BをAにキャストしたもの~~ 20 21 var a = A() 22 a.a → 1 23 a.b → 2 24

現状だと、↓のようなキャストする関数を1個作って対応しています。
```Swift
func AtoB (a : A) -> B{
var b = B()
b.a = a.a
b.b = a.b
}
// 逆も然り

CodableやJSONデコードなど調べたのですが「対応するキーがあればそこに値を代入する」のような動きがすぐ見た感じでは難しそうでしたので、こちらに投稿しました。 初投稿につき、不得手ではありますが回答いただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

shiokara さんの回答とそれに寄せられていた garubor01 さんの補足情報を拝見しました。ある型を別の型として扱おうとしたとき、Swift 通称 変換イニシャライザー(今回であれば Narrowing Type Conversion)と呼ばれる、別の型から自分自身の型に変換するためのイニシャライザー** を型ごとに実装することを推奨していたりするので、一般的には shirokara さんの示されたコードがお行儀の良いコードになるように感じます。

その上で、garubor01 さんの補足が興味深く思えましたので、Swift でそれっぽいものを書くとしたらどんなふうになるか試してみました。そうしたところ、最初のご質問で触れられていた Codable を使うと比較的それっぽく、簡単なコードで実現できた様子なので紹介させていただきますね。

□ キャストに対応していることを示すプロトコルを用意する

Swift では、型を跨いで何かしらのルールを適用するためには(今回であればキャスト可能とするためには)プロトコルという仕組みが不可欠になりがちなので、今回は Castable というプロトコルを用意してみました。

swift

1protocol Castable : Codable { 2}

変換には Codable の仕組みを使おうと思うため、あらかじめそれを継承したプロトコルにしておきます。

なお、今回は Codable というプロトコルにさえ適用された型であれば大丈夫なので、フリーな関数(グローバル関数)でキャストするならなくても実現可能ですけど、Swift らしくよりスマートに見せるために用意しています。

□ キャスト関数を用意しておく

今回、Castable プロトコルに準拠した型であれば、どんな型でも別の型へすぐにキャストできる仕組みを提供したいので、Castable プロトコル自身に casting というメソッドをディフォルト実装しておきます。

swift

1extension Castable { 2 3 func casting<R: Castable>() -> R { 4 5 let data = try! JSONEncoder().encode(self) 6 return try! JSONDecoder().decode(R.self, from: data) 7 } 8}

ここで使用する JSONDecoderJSONEncoder は、Swift のコアライブラリー Foundation をインポートすると使える機能で、これが今回にはとても都合の良いことに、オプショナル型のプロパティーであれば、対象のキーがある時には値を設定し、対象のキーがない時は nil として扱ってくれます。

そのため、上記のようにとてもシンプルにコードを書くことができました。

□ キャストしてみる

これで準備は整ったはずなので、次のように Castable に対応した型を2つ作って実験してみます。

swift

1struct A : Castable { 2 3 var a: Int? 4 var b: Int? 5} 6 7struct B : Castable { 8 9 var a: Int? 10 var b: Int? 11 var c: Int? 12}
実験1

swift

1var a = A() 2a.a = 1 3a.b = 2 4 5let b = a.casting() as B 6 7dump(b.a) // 1 8dump(b.b) // 2 9dump(b.c) // nil
実験2

swift

1var b = B() 2b.a = 1 3b.b = 2 4b.c = 3 5 6let a = b.casting() as A 7 8dump(a.a) // 1 9dump(a.b) // 2
所感

キャストの際に casting() as X みたいに書かないといけないですけれど、それでも Swift としては最短な感じで表現できているのと、型ごとの事前準備は Casatable プロトコルに準拠させるだけで済むので、だいぶ善戦した感じのコードになっているような気がします。

ただ、冒頭でも触れたように、Swift のガイドライン的にはあまり行儀の良くないコードのようにも思えますので、誰かと共同で制作するプロジェクトでは使用を控えておくのが無難かもしれないです。

そう思いはしたものの、改めて眺めてみると今回に自分が挙げた例でもそこまで誤解を招くようなコードになっていない気もしてきたので、もしこのコードに携わる誰もが、ここでいうキャストの動作が今回の質問で記されている動きをするものという認識をズレなく持っている場合であれば使っても大丈夫かもしれない気もしてきました。

投稿2021/09/04 23:07

編集2021/09/04 23:27
TomohiroKumagai

総合スコア441

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

garubor01

2021/09/05 17:12

TomohiroKumagai様、回答ありがとうございます。 そしてこれ多分、自分がCodableのことをまともに理解していれば、発生しなかった質問ですね・・! ご丁寧にソースコードを記載いただきありがとうございます。 > 一般的には shirokara さんの示されたコードがお行儀の良いコードになるように感じます。 まさにそのような認識です。 お二人とも、この度は大変お世話になりました。
guest

0

構造体の名前がややこしかったので変えてます。
initでAならB、BならAを引数で受け取るってやり方があります。

Swift

1 2struct ObjecrtA { 3 var a: Int? 4 var b: Int? 5 6 init(a: Int? = nil, b: Int? = nil) { 7 self.a = a 8 self.b = b 9 } 10 11 init(convert objrctB: ObjecrtB) { 12 self.a = objrctB.a 13 self.b = objrctB.b 14 } 15} 16 17struct ObjecrtB { 18 var a: Int? 19 var b: Int? 20 var c: Int? 21 22 init(a: Int? = nil, b: Int? = nil, c: Int? = nil) { 23 self.a = a 24 self.b = b 25 self.c = c 26 } 27 28 init(convert objrctA: ObjecrtA) { 29 self.a = objrctA.a 30 self.b = objrctA.b 31 self.c = nil 32 } 33}

使い方

Swift

1// Aを生成してBにキャスト 2var objectA = ObjecrtA() 3objectA.a = 1 4objectA.b = 2 5//let objectA = ObjecrtA(a: 1, b: 2) もう言う書き方もできる 6 7let objectB = ObjecrtB(convert: objectA) 8 9 10// Bを生成してAにキャスト 11var objectB = ObjecrtB() 12objectB.a = 1 13objectB.b = 2 14objectB.c = 3 15//let objectA = ObjecrtA(a: 1, b: 2, c: 3) もう言う書き方もできる 16 17let objectA = ObjecrtB(convert: objectB)

投稿2021/09/03 07:11

shiokara

総合スコア95

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

garubor01

2021/09/04 00:55

shiokara様、ご回答ありがとうございます。 そして大変申し訳ありません。 自分の質問の仕方がとても悪かったことをここに申し上げておきます。 自分の大目的 - 構造体Aと構造体Bをキャストしたい - キャストとは、キー名(+型)が一致するパラメータがあればそこに同一のものがあれば代入する - なければnilを入れておく - ないものが入ってこようとした場合には、無視する というものでした。 また、shiokara様にご教示いただいたように、ある構造体AからBへのキャストイニシャライザ(または関数)を作成して対応することは可能かと思います。 ただ、とても怠惰な自分ゆえ - 構造体AからBのキャストイニシャライザ作っても、AからCの場合もまた作らないといけないなぁ - やりたいこと - 対応するキー値があったら代入 - なければnil - 対応しないキー値があったら無視 のようなことをあらゆる構造体で共通で使用できる関数がないかなぁ・・と考えていました。 使用イメージは、質問頭のようなイメージです。 ( var b = ~~AをBにキャストしたもの~~ var a = ~~BをAにキャストしたもの~~ )
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問