Golang中的类型转换 [英] Typecasting in Golang

查看:128
本文介绍了Golang中的类型转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读以下文章: https://www.ribice.ba/golang-enums/

其中一个代码示例中定义了一个函数:

func (lt *LeaveType) UnmarshalJSON(b []byte) error {
    // Define a secondary type to avoid ending up with a recursive call to json.Unmarshal
    type LT LeaveType;
    var r *LT = (*LT)(lt);
    err := json.Unmarshal(b, &r)
    if err != nil{
        panic(err)
    }
    switch *lt {
    case AnnualLeave, Sick, BankHoliday, Other:
        return nil
    }
    return errors.New("Inalid leave type")
}

在此示例中,var r *LT = (*LT)(lt);的语法是什么?

解决方案

Go从技术上讲没有播报,而没有转换.显式转换的语法为T(x),其中T是某种类型,而x是可转换为该类型的某些值.有关详细信息,请参见 Go规范中的转化.

从函数的声明中可以看到:

func (lt *LeaveType) UnmarshalJSON(b []byte) error {

lt本身具有指向LeaveType 的指针类型,而UnmarshalJSON是类型*LeaveType接收器函数.当encoding/json包要设置的变量的类型为LeaveType(或*LeaveType,在这种情况下,包将自行创建LeaveType变量)时,encoding/json包将调用该函数来解码输入的JSON.

正如代码中的注释所表明的那样,代码的作者现在想让encoding/json代码解组JSON 的字符串,就好像中没有函数UnmarshalJSON一样.但是这里有 函数UnmarshalJSON,因此,如果我们只是调用encoding/json代码而没有一点点麻烦,那么encoding/json就会再次调用此函数,从而导致无限递归.

通过定义其内容与现有类型LeaveType完全相同的 new 类型LT,我们最终得到一个没有的新类型接收器功能.在此类型的实例(或指向该类型的指针)上调用encoding/json不会调用*LeaveType接收器,因为LT是不同的类型,即使其内容完全匹配.

我们可以这样做:

func (lt *LeaveType) UnmarshalJSON(b []byte) error {
    type LT LeaveType
    var r LT
    err := json.Unmarshal(b, &r)
    if err != nil {
        panic(err)
    }
    // ...
}

这将填写r,其大小和形状与任何LeaveType变量相同.然后,我们可以使用填充的r设置*lt:

*lt = LeaveType(r) // an ordinary conversion

之后,我们可以像以前一样继续操作,使用*lt作为值.但这意味着UnmarshalJSON必须设置一个临时变量r,然后我们必须将其复制到其最终目的地.相反,为什么不做一些设置,以便UnmarshalJSON填写目标变量,但使用我们选择的类型?

这就是 for 的语法.它不是最短版本:正如CeriseLimón指出的那样,它的拼写方式更短(通常首选较短的拼写方式).必须将(*LT)(lt)中的第一组括号绑定到LT,因为*LT(lt)具有错误的绑定:将*(指向指向的指针部分)绑定到LT:这意味着同一件事*(LT(lt)),这不是我们想要的.

I was reading this following article: https://www.ribice.ba/golang-enums/

There is a function defined in one of the code samples:

func (lt *LeaveType) UnmarshalJSON(b []byte) error {
    // Define a secondary type to avoid ending up with a recursive call to json.Unmarshal
    type LT LeaveType;
    var r *LT = (*LT)(lt);
    err := json.Unmarshal(b, &r)
    if err != nil{
        panic(err)
    }
    switch *lt {
    case AnnualLeave, Sick, BankHoliday, Other:
        return nil
    }
    return errors.New("Inalid leave type")
}

What is the syntax var r *LT = (*LT)(lt); doing in this example?

解决方案

Go technically does not have casts but rather conversions. The syntax for an explicit conversion is T(x) where T is some type and x is some value that is convertible to that type. See Conversions in the Go specification for details.

As you can see from the function's declaration:

func (lt *LeaveType) UnmarshalJSON(b []byte) error {

lt itself has type pointer to LeaveType and UnmarshalJSON is a receiver function for type *LeaveType. The encoding/json package will call such a function to decode input JSON when the variable that the package would like to set has type LeaveType (or *LeaveType—the package will create the LeaveType variable itself in this case).

As the comment in the code says, the author of the code would now like to have the encoding/json code unmarshal the JSON as if there weren't a function UnmarshalJSON. But there is a function UnmarshalJSON, so if we just invoke the encoding/json code without a little bit of trickery, encoding/json will just call this function again, leading to infinite recursion.

By defining a new type LT whose contents are exactly the same as the existing type LeaveType, we end up with a new type that does not have a receiver function. Invoking the encoding/json on an instance of this type (or of a pointer to this type) won't call the *LeaveType receiver, because LT is a different type, even though its contents match up exactly.

We could do this:

func (lt *LeaveType) UnmarshalJSON(b []byte) error {
    type LT LeaveType
    var r LT
    err := json.Unmarshal(b, &r)
    if err != nil {
        panic(err)
    }
    // ...
}

This would fill in r, which has the same size and shape as any LeaveType variable. Then we could use the filled-in r to set *lt:

*lt = LeaveType(r) // an ordinary conversion

after which we could keep going as before, using *lt as the value. But this means that UnmarshalJSON had to set a temporary variable r, which we then had to copy to its final destination. Why not, instead, set up something so that UnmarshalJSON fills in the target variable, but using the type we chose?

That's what the syntax here is for. It's not the shortest version: as Cerise Limón noted, there is a shorter way to spell it (and that shorter spelling is generally preferred). The first set of parentheses in (*LT)(lt) is required to bind the *—the pointer to part—to the LT, as *LT(lt) has the wrong binding: it means the same thing as *(LT(lt)) which is not what we want.

这篇关于Golang中的类型转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆