dongxie2756
2018-09-04 10:34 阅读 68
已采纳

将接口声明为其类型

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 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
    
    点赞 评论 复制链接分享

相关推荐