gpt4 book ai didi

go - 创建常量类型并限制类型的值

转载 作者:IT老高 更新时间:2023-10-28 13:02:19 25 4
gpt4 key购买 nike

我有一个关于限制为特定值的常量类型以及如何在 Go 中实现这一点的问题。假设我创建了一个类型 unary 它有两个常量值 Positive(1)Negative(-1) 我想限制用户该类型 (unary) 从创建 unary 类型的其他值。我是否可以通过创建一个包并使值 PositiveNegative 可见并将 unary 类型限制为包含包来实现这一点?例如,请参见下面的代码

package unary

type unary int////not visible outside of the package unary

const (
Positive unary = 1//visible outside of the package unary
Negative unary = -1//visible outside of the package unary
)

func (u unary) String() string {//visible outside of the package unary
if u == Positive {
return "+"
}
return "-"
}

func (u unary) CalExpr() int {//visible outside of the package unary
if u == Positive {
return 1
}
return -1
}

这是将类型限制为某些常量值的正确方法吗?

最佳答案

缺陷

您提出的解决方案并不像您希望的那样安全。可以使用无类型整数常量来创建 unary 的新值,该值具有与 1-1 不同的 int 值.看这个例子:

p := unary.Positive
fmt.Printf("%v %d\n", p, p)

p = 3
fmt.Printf("%v %d\n", p, p)

输出将是:

+ 1
- 3

我们可以改变p的值来存储int3显然不等于Positive 也不是 Negative。这是可能的,因为 Spec: Assignability:

A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:

  • ...
  • x is an untyped constant representable by a value of type T.

3 是一个无类型常量,它可以由一个 unary 类型的值表示,该值具有底层类型 int

由于上述原因,在 Go 中,您不能拥有“外部”包无法为其创建新值的“安全”常量。因为如果你想在你的包中声明 constants,你只能使用具有“无类型”版本的表达式——其他包也可以在赋值中使用它(就像在我们的示例中一样)。

未导出的结构

如果你想实现“安全”部分,你可以使用未导出的struct,但是它们不能在constant declarations中使用。 .

例子:

type unary struct {
val int
}

var (
Positive = unary{1}
Negative = unary{-1}
)

func (u unary) String() string {
if u == Positive {
return "+"
}
return "-"
}

func (u unary) CalExpr() int {
return u.val
}

试图改变它的值:

p := unary.Positive

p.val = 3 // Error: p.val undefined (cannot refer to unexported field or method val)

p = unary.unary{3} // Error: cannot refer to unexported name unary.unary
// Also error: implicit assignment of unexported field 'val' in unary.unary literal

请注意,由于我们现在使用的是 struct,因此我们可以通过将值的 string 表示形式添加到 struct :

type unary struct {
val int
str string
}

var (
Positive = unary{1, "+"}
Negative = unary{-1, "-"}
)

func (u unary) String() string { return u.str }

func (u unary) CalExpr() int { return u.val }

请注意,这个解决方案仍然有一个“缺陷”:它使用导出的全局变量(更准确地说是包级变量),其值可以被其他包更改。确实,其他包无法创建和分配 new 值,但它们可以使用现有值这样做,例如:

unary.Positive = unary.Negative

如果您想保护自己免受此类滥用,您还必须不导出此类全局变量。然后当然你必须创建导出函数来公开这些值,例如:

var (
positive = unary{1}
negative = unary{-1}
)

func Positive() unary { return positive }

func Negative() unary { return negative }

然后获取/使用这些值:

p := unary.Positive()

界面

如果您打算为“常量”使用接口(interface)类型,则必须小心。在 Kaveh Shahbazian 的回答中可以看到一个例子。一个未导出的方法用于阻止其他人实现接口(interface),给你一种别人真的无法实现的错觉:

type Unary interface {
fmt.Stringer
CalExpr() int
disabler() // implementing this interface outside this package is disabled
}

var (
Positive Unary = unary(1) // visible outside of the package unary
Negative Unary = unary(-1) // visible outside of the package unary
)

type unary int // not visible outside of the package unary

func (u unary) disabler() {}

func (u unary) String() string { /* ... */ }

func (u unary) CalExpr() int { /* ... */ }

但情况并非如此。使用一个肮脏的技巧,这可以被规避。导出的 Unary 类型可以嵌入,并且可以使用现有值来实现接口(interface)(以及未导出的方法),我们可以添加自己的导出方法的实现,做/返回我们想要的任何东西。

下面是它的样子:

type MyUn struct {
unary.Unary
}

func (m MyUn) String() string { return "/" }

func (m MyUn) CalExpr() int { return 3 }

测试它:

p := unary.Positive
fmt.Printf("%v %d\n", p, p)

p = MyUn{p}
fmt.Printf("%v %d\n", p, p.CalExpr())

输出:

+ 1
/ 3

特殊情况

正如沃尔克在他的评论中提到的,在你的特殊情况下,你可以使用

type unary bool

const (
Positive unary = true
Negative unary = false
)

由于bool 类型有两个可能的值:truefalse,我们都用过了。所以没有其他值可以被“利用”来创建我们常量类型的其他值。

但要知道,这只能在常量的数量等于该类型的可能值的数量时使用,因此这种技术的可用性非常有限。

还请记住,当需要 unary 类型时,这并不能防止此类误用,并且有人不小心传递了诸如 truefalse 之类的无类型常量.

关于go - 创建常量类型并限制类型的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37385007/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com