Как неструктурный тип может расширять функции структуры

# #go #go-gorm

Вопрос:

обновление: Хорошо, я сделал это неправильно. CustomJSONType функции s не могут получить доступ к полям структуры, в которую они встроены, для которых оба User и IntList должны определить Scan и Value самостоятельно или попробовать другие обходные пути.


оригинальное сообщение 2021-6-21

Я использую GORM и пытаюсь определить некоторые пользовательские типы, такие как User и IntList .

 type User struct {
    Id      int
    Name    string
    Email   string
}

type IntList []int
 

И, как известно, пользовательский тип GORM должен реализовывать Value и Scan функции. Вот что я сделал — я определил CustomJSONType , что реализует Value и Scan как это:

 type CustomJSONType struct {}

func (t *CustomJSONType) Scan(src interface{}) error {
    bytes, ok := src.([]byte)
    if !ok {
        return errors.New("invalid input type")
    }
    err := json.Unmarshal(bytes, t)
    if err != nil{
        return err
    }
    return nil
}

func (t CustomJSONType) Value() (driver.Value, error) {
    bytes, err := json.Marshal(t)

    if err != nil {
        return []byte{}, err
    }
    return bytes, err
}

func (t CustomJSONType) GormDataType() string {
    return "json"
}
 

Для оснащения User Value и Scan все , что мне нужно сделать, это добавить одну строку в определение User :

обновление: это не будет работать, так как CustomJSONType не может получить доступ к полям User

 type User struct {
    CustomJSONType
    Id      int
    Name    string
    Email   string
}
 

Однако я понятия не имею, как это расширить IntList CustomJSONType . Я должен явно определить Scan и Value для IntList . Даже в лучшем случае я могу обернуть реализацию Value и Scan как независимые функции, но все равно придется писать подписи для Scan и Value в IntList . Есть какие-нибудь предложения, чтобы определить IntList так же просто, как User ?

Комментарии:

1. Типы структур-это единственные конкретные типы, в которые допускается встраивание. Поскольку IntList это не тип структуры, вы не можете встроить CustomJSONType в нее a, как вы это сделали для User .

2. Чего вы пытаетесь достичь с Scan Value помощью реализаций и для пустого типа структуры? Если вы ожидаете CustomJSONType «наследовать» поля структуры, в которую она встроена, вы должны знать, что это не то, что делает встраивание. В Go нет расширения типа.

3.В принципе, вы должны явно определить Scan и Value для обоих типов — IntList и. User Это CustomJSONType бесполезно, все, что он делает, это удовлетворяет интерфейсу, но реализация ничего не делает, так t как в обоих методах есть пустая структура или указатель на пустую структуру.

4. @mkopriva спасибо! Я не тестировал свой код во время выполнения.

5. @mkopriva Вы можете подделать расширение метода в Go, внедрив пустую структуру значений в качестве первого элемента с помощью метода с приемником указателя, в котором вы используете unsafe.Pointer для изменения типа приемника на то, в что встроена ваша структура. По крайней мере, до Go 2: github.com/golang/go/issues/18617 Не рекомендуется, но это работает.

Ответ №1:

В голанге нет понятия «наследовать», а есть только комбинация. Не нравится обычный язык ООП, структура CustomJSONType не может получить доступ к данным из IntList и User , поэтому метод CustomJSONType.Value не может сериализовать другие типы, кроме самого себя. Если вы хотите реализовать метод один раз и использовать его в любом месте, я приведу ниже идею.

 package main

import (
    "database/sql/driver"
    "encoding/json"
    "errors"
    "fmt"
)

type CustomJSONType struct {
    data interface{}
}

func (t *CustomJSONType) Scan(src interface{}) error {
    bytes, ok := src.([]byte)
    if !ok {
        return errors.New("invalid input type")
    }
    err := json.Unmarshal(bytes, t.data)
    if err != nil{
        return err
    }
    return nil
}

func (t CustomJSONType) Value() (driver.Value, error) {
    bytes, err := json.Marshal(t.data)

    if err != nil {
        return []byte{}, err
    }
    return bytes, err
}

func (t CustomJSONType) GormDataType() string {
    return "json"
}

type User struct {
    Id      int
    Name    string
    Email   string
}

type Project struct {
    user CustomJSONType
}

func (p *Project) SetUser(u User) {
    p.user.data = u
}

func (p *Project) GetUser() User {
    user := p.user.data.(User)
    return user
}

 

Ответ №2:

Не уверен, правильно ли я вас понял. Но вы можете объединить несколько типов, включая массив int, без создания структуры. Они остаются типом и доступны в функциях. Но объединение типов требует структуры, поэтому вы не можете напрямую использовать адресацию массива.

 type IntArr []int
type AnyStruct struct{
    Uses bool
}
type TestIntArrStruct struct{
    IntArr
    AnyStruct
}
func(arr *IntArr) Add(i ...int){
    *arr = append(*arr, i...)
}
func(anyStruct *AnyStruct) Invert(){
    anyStruct.Uses = !anyStruct.Uses
}
var a TestIntArrStruct
a.Add(1,2,3)
a.Invert()
 

Вы можете увидеть это здесь:
https://play.golang.org/p/MF5eMcgmeak