dongxie2756 2018-09-04 10:34
浏览 69
已采纳

将接口声明为其类型

I can't gracefully get pixels of an image as array in general case.

f, err := os.Open(imgPath)
check(err)
defer f.Close()
img, _, err := image.Decode(bufio.NewReader(f))
check(err)
pixels, err := getPixels(img)
check(err)
// Logic with pixels.

Now function getPixels looks like this:

func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(*image.NRGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Alpha16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.CMYK); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Gray16); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.NRGBA64); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.Paletted); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA); ok {
        return i.Pix, nil
    } else if i, ok := img.(*image.RGBA64); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}

But I think this is ugly. Golang knows type of image and I would prefer something like this:

func getPixels(img image.Image) ([]uint8, error) {
    if i, ok := img.(eval(fmt.Sprintf("%T", img))); ok {
        return i.Pix, nil
    }
    return nil, fmt.Errorf("unknown image type %T", img)
}

I also can't assert to reflect.TypeOf(img). Maybe there is a way to get type from reflect.Type interface?

  • 写回答

1条回答 默认 最新

  • duanchuo7741 2018-09-04 10:46
    关注

    Your big if ... else structure could be simplified by using a type switch like this:

    func getPixels(img image.Image) ([]uint8, error) {
        switch i := img.(type) {
        case *image.NRGBA:
            return i.Pix, nil
        case *image.Alpha:
            return i.Pix, nil
        case *image.Alpha16:
            return i.Pix, nil
        case *image.CMYK:
            return i.Pix, nil
            // ...
        }
        return nil, fmt.Errorf("unknown image type %T", img)
    }
    

    Where you still have to list all possible types, but it's nicer.

    Since all image implementations are struct pointers having a field named Pix, you may use reflection to get that field. This implementation will handle future image implementations without any change (if they will also be structs with a Pix field).

    This is how it would look like:

    func getPix(img image.Image) ([]uint8, error) {
        v := reflect.ValueOf(img)
        if v.Kind() == reflect.Ptr {
            v = v.Elem()
        }
    
        if v.Kind() == reflect.Struct {
            pv := v.FieldByName("Pix")
            if pv.IsValid() {
                if pix, ok := pv.Interface().([]uint8); ok {
                    return pix, nil
                }
            }
        }
    
        return nil, fmt.Errorf("unknown image type %T", img)
    }
    

    Testing it:

    fmt.Println(getPix(&image.NRGBA{}))
    fmt.Println(getPix(&image.RGBA{}))
    
    type unknownImage struct{ image.Image }
    fmt.Println(getPix(unknownImage{}))
    

    Output (try it on the Go Playground):

    [] <nil>
    [] <nil>
    [] unknown image type main.unknownImage
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器