Go语言 如何比较通用编号类型

qgelzfjb  于 2023-04-27  发布在  Go
关注(0)|答案(1)|浏览(132)

我有一个验证功能积极,它的作品,但看起来很丑.

type Positiver interface {
    decimal.Decimal | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64
}

//nolint:cyclop
func Positive[T Positiver](value T, name string, errs *[]error) {
    addError := func() {
        err := fmt.Errorf(`%s %w, but it's %v`, name, failures.ShouldBePositive, value)
        *errs = append(*errs, err)
    }

    const prescision = 8

    switch val := any(value).(type) {
    case decimal.Decimal:
        if val.IsNegative() || val.IsZero() {
            err := fmt.Errorf(`%s %w, but it's %s`, name, failures.ShouldBePositive, val.StringFixedBank(prescision))
            *errs = append(*errs, err)
        }

        return
    case int:
        if val <= 0 {
            addError()
        }
    case int64:
        if val <= 0 {
            addError()
        }
    case int32:
        if val <= 0 {
            addError()
        }
    case int16:
        if val <= 0 {
            addError()
        }
    case int8:
        if val <= 0 {
            addError()
        }
    case uint:
        if val <= 0 {
            addError()
        }
    case uint64:
        if val <= 0 {
            addError()
        }
    case uint32:
        if val <= 0 {
            addError()
        }
    case uint16:
        if val <= 0 {
            addError()
        }
    case uint8:
        if val <= 0 {
            addError()
        }
    case float32:
        if val <= 0 {
            addError()
        }
    case float64:
        if val <= 0 {
            addError()
        }
    default:
        panic(fmt.Sprintf(`%T is not supported type`, val))
    }
}

我知道使用[]error是一种不好的方法,最好是返回一个 Package 的错误。但这是一个兼容性问题。
我试着这样做:

func Positive[T Positiver](value T, name string, errs *[]error) {
    switch val := any(value).(type) {
    case decimal.Decimal:
        if val.IsNegative() || val.IsZero() {
            err := fmt.Errorf(`%s %w, but it's not`, name, failures.ShouldBePositive)
            *errs = append(*errs, err)
        }

        return
    case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
        if val.(int64) <= 0 {
            err := fmt.Errorf(`%s %w, but it's not`, name, failures.ShouldBePositive)
            *errs = append(*errs, err)
        }

        return
    case float32, float64:
        if val.(float64) < 0 {
            err := fmt.Errorf(`%s %w, but it's not`, name, failures.ShouldBePositive)
            *errs = append(*errs, err)
        }

        return
    default:
        panic(fmt.Sprintf(`%T is not supported type`, val))
    }
}

但是这种方法返回了一个错误:测试死机:接口转换:interface {}是int,不是int 64
比较该值是否超过零的更好方法是什么?

3pmvbmvn

3pmvbmvn1#

您的代码不适用于“接口转换:interface {}是int,而不是int 64”,因为在多类型case中,类型开关变量val保持其原始类型。另请参阅:golang multiple case in type switch详细信息。
所以在这种情况下,为了使用顺序运算符,你确实必须Assert某个东西。那个“东西”**可能是一个类型参数。

case int, int8, int16, int32, int64:
        if val.(T) <= 0 {
            // ...
        }

但是这段代码仍然不能使用order运算符,因为约束Positive包含decimal.Decimal,它不支持排序。

尝试为decimal.Decimal写一个case,为其他数值类型写一个case也不会很好,因为你没有一个好的方法来减少类型约束的类型集。你正在为每个类型写一个case。有一天Go可能允许在类型开关中使用联合约束。
你现在可以做的是静态地处理decimal.Decimal和其他数值类型。你可以使用constraints包中的类型来避免重新声明所有内容:SignedUnsignedFloat。那么一个只有数值类型的朴素函数就像这样简单:

func StrictlyPositive[T Signed | Unsigned | Float](v T) bool {
    return v > 0
}

但是对于浮点数,使用<是不够的。浮点数变量也可以是NaN或+/-infinity。你必须决定如何排序NaN;无穷大有符号位,但在我看来,最好使用math.IsInf,这样就不会在顺序运算符后面隐藏东西。

所以总而言之,我认为这个函数使用反射会更好,反射可能会更慢,但代码并不完全糟糕。下面是你的示例的简化版本:

func CheckPositive[T Positive](value T) string {
    switch val := any(value).(type) {
    case decimal.Decimal:
        if val.IsNegative() || val.IsZero() {
            return "non positive decimal"
        }

    case int, int8, int16, int32, int64:
        if reflect.ValueOf(val).Int() <= 0 {
            return "non positive signed"
        }

    case uint, uint8, uint16, uint32, uint64:
        if reflect.ValueOf(val).Uint() == 0 {
            return "non positive unsigned"
        }

    case float32, float64:
        f := reflect.ValueOf(val).Float()
        switch {
        case math.IsNaN(f):
            return "NaN float"
        case math.IsInf(f, -1):
            return "negative infinite"
        case math.IsInf(f, 1):
            // do nothing
        default:
            // not a NaN and not an Infinite
            if f <= 0.0 {
                return "negative float"
            }
        }

    default:
        panic(fmt.Sprintf(`%T is not supported type`, val))
    }
    return "positive"
}

相关问题