2016-05-09 4 views
2

私はこのようになります構造体があります。インターフェイスを使用して2つ以上のstruct型を使用すると、コードをより使いやすくすることはできますか?

type BetaKey struct { 
    Id   int64 `json:"-"` 
    IssuerId  int64 `json:"-"   db:"issuer_id"` 
    SubjectEmail string `json:"-"   db:"subject_email"` 
    IssuedTime int64 `json:"-"   db:"issued_time"` 
    ExpiryTime int64 `json:"expiryTime" db:"expiry_time"` 
    Betakey  string `json:"accessKey"` 
    Description string `json:"description"` 
} 

を、同じパッケージ内に、私はBetaKeyのスライスを返す関数があります:私はこの機能を書き換えるため

func buildResults(query string) ([]BetaKey, error) { 
    results := []BetaKey{} 
    rows, err := sql.DB.Queryx(query) 
    if err != nil { 
     return results, err 
    } 
    defer rows.Close() 
    for rows.Next() { 
     var bk BetaKey 
     err := rows.StructScan(&bk) 
     if err != nil { 
      return results, err 
     } 
     results = append(results, bk) 
    } 
    err = rows.Err() 
    if err != nil { 
     return results, err 
    } 
    return results, nil 
} 

はそれが可能ですその結果、クエリ文字列はBetaKeyとなり、interface{}となり、スライスはinterface{}になります。これは、コピーする代わりにコードを再利用できるため、すべてのパッケージに貼り付けることができますが、唯一の違いは、名前彼はその変更を構造化する。

これは可能ですか?また、これはアドバイスですか?そうでない場合は、なぜですか?

+0

はい可能です。 Golangのリフレクションについて学ぶだけです。 – Apin

+0

私はリフレクトで構造体の名前を取得する方法を知っています。そして私は。(タイプ)の構文でインターフェイスを変換できることを知っています。しかし、私はそれを実現するために上記の関数を書き直す方法はまだありません。 – Alex

答えて

1

ジェネリック医薬品は、そのようなものを実装するために使用することができますが、移動は、ジェネリックをサポートしていません。あなたがGoで欲しいことをするには、リフレクションを使用する必要があります。

1つの追加パラメータ、たとえばreflect.Typeを使用するように関数を変更して、個々の行をロードする値のタイプを指定することができます。

次に、reflect.New()を使用して、この型の新しい値を作成し、この型へのポインタを取得できます。 Value.Interface()を使用して、の値からinterface{}の型としてポインタ値を取得できます。このinterface{}のポインタをラップすると、Rows.StructScan()に渡すことができます。

そして、あなたは結果のスライスが非ポインタ値を保持したい場合、あなたは(interface{}として構造体の値を抽出するために、別のreflect.Interface())尖った値を取得するためにreflect.Indirect()を使用することができます。

例コード:

func buildResults(query string, t reflect.Type) ([]interface{}, error) { 
    results := []interface{}{} 
    rows, err := sql.DB.Queryx(query) 
    if err != nil { 
     return results, err 
    } 
    defer rows.Close() 
    for rows.Next() { 
     val := reflect.New(t) 
     err := rows.StructScan(val.Interface()) 
     if err != nil { 
      return results, err 
     } 
     i_ := reflect.Indirect(val) 
     result = append(result, i_.Interface()) 
    } 
    err = rows.Err() 
    if err != nil { 
     return results, err 
    } 
    return results, nil 
} 

その心はforブロックです:

val := reflect.New(t)     // A pointer to a new value (of type t) 
err := rows.StructScan(val.Interface()) // Pass the pointer to StructScan 
if err != nil { 
    return results, err 
} 
i_ := reflect.Indirect(val)    // Dereference the pointer 
result = append(result, i_.Interface()) // And add the non-pointer to the result slice 

ここであなたがそれをテストすることができます方法は次のとおりです。

type BetaKey struct { 
    Id string 
    Name string 
} 

type AlphaKey struct { 
    Id  string 
    Source string 
} 

r, err := buildResults("", reflect.TypeOf(AlphaKey{})) 
fmt.Printf("%T %+v %v\n", r[0], r, err) 

r, err = buildResults("", reflect.TypeOf(BetaKey{})) 
fmt.Printf("%T %+v %v\n", r[0], r, err) 

出力:

main.AlphaKey [{Id:aa Source:asource} {Id:aa Source:asource} {Id:aa Source:asource}] <nil> 
main.BetaKey [{Id:aa Name:aname} {Id:aa Name:aname} {Id:aa Name:aname}] <nil> 

Go Playgroundでお試しください。

上記溶液が要素静的タイプinterface{}であろうと、それらの動的タイプはreflect.Type引数で指定されたものであろう[]interface{}の型の値を返します。あなたがタイプとそれを呼び出すのであれば、たとえば:

bt := reflect.TypeOf(BetaKey{}) 

結果スライスの値が動的型BetaKeyを持つことになりますので、安全に、このようにそれらをtype assertことができます。

results, err := buildResults("some query", bt) 
if err == nil { 
    for _, v := range results { 
     key := v.(BetaKey) 
     // key is of type BetaKey, you may use it like so 
    } 
} else { 
    // handle error 
} 

反射の詳細については、ブログ記事を読む:

The Go Blog: The Laws of Reflection

+0

ありがとうございました!あなたのプロフィールにスタークラフトが好きです。あなたは万が一にiccupをプレイしていませんでしたか? ^^ – Alex

+0

私はjsonにしたいと思っています。結果を整理するには、最初に。(タイプ)の構文を使用してinterface {}タイプをBetaKeyに変換する必要がありますか?またはjsonに可能ですか?インタフェースをマーシャリングしますか? – Alex

+0

@Alex In Starcraft(Broodwar)私は「BGHの男」(Big Game Huntersを演じた)で、iccupはいなかったが、今日は私がプレイするときに、私はもちろんStarCraft IIしかプレイしない。 – icza

1

私は、SQLの行の代わりにjsonを使用して少しの例を書いています。あなたは、このコードから開発しようとすることができます:

package main 

import (
    "fmt" 
    "reflect" 
    "encoding/json" 
) 

type A struct { 
    AField int `json:"a"` 
} 

type B struct { 
    BField string `json:"b"` 
} 

func build(str string, typ reflect.Type) interface{} { 
    results := reflect.MakeSlice(reflect.SliceOf(typ), 0, 10) 
    for i:=0; i < 5; i++ { 
     res := reflect.New(typ) 
     json.Unmarshal([]byte(str), res.Interface()) 
     results = reflect.Append(results, res.Elem()) 
    } 
    return results.Interface(); 
} 

func main() { 
    a := build("{ \"a\": 15 }", reflect.TypeOf(&A{})) 
    fmt.Printf("%T : %V\n", a, a) 
    b := build("{ \"b\": \"my string\" }", reflect.TypeOf(&B{})) 
    fmt.Printf("%T : %V\n", b, b) 
} 

Golang Playground

+0

スライスの長さと容量がわからない場合はどうしたらいいですか?私はちょうどスライスを作成し、長さと容量について何も言わずに動的にそれらを追加するのに慣れています – Alex

+0

問題はありません、任意の数を置くことができます。それは自動的に成長する。 – Apin

関連する問題