创建一个常量类型并限制类型的值
我有一个关于将常量类型限制为某些值的问题,以及如何在Go中完成该操作。说我创建类型unary
,其具有两个常数值Positive(1)
和Negative(-1)
与我想限制该类型(用户unary
从创建类型的其它值)unary
。我是否可以通过创建一个程序包并使其值Positive
和Negative
可见性以及将类型unary
限制为包含的程序包来实现此目的?例如,请参见下面的代码
package unarytype 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
该新值的int
值不同于1
或-1
。请参阅以下示例:
p := unary.Positivefmt.Printf("%v %d\n", p, p)
p = 3
fmt.Printf("%v %d\n", p, p)
输出将是:
+ 1- 3
我们可以更改其p
值来存储显然不等于或不等于的int
值。这是可能的,因为规格:可分配性:3``Positive``Negative
在以下任何一种情况下,值
x
都可以 分配 给类型的变量T
(“
x
可以分配给T
”):
- …
x
是可由type值表示的无类型常量T
。
3
是一个无类型的常量,可以用unary
具有基础type 的type值表示int
。
在Go中,由于上面提到的原因,您不能拥有“安全”常量,而“局外人”软件包无法为其创建新值。因为如果要在包中声明 常量
,则只能使用具有“未类型化”版本的表达式,其他表达式也可以在赋值中使用这些表达式(就像我们的示例一样)。
未导出的结构
如果要实现“安全”部分,则可以使用unexported
struct
,但是不能在常量声明中使用它们。
例:
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.Positivep.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 }
请注意,此解决方案仍然存在“缺陷”:它使用导出的全局变量,其值可以由其他程序包更改。确实其他软件包不能创建和分配 新
值,但是它们可以使用现有值来创建和分配 新 值,例如:
unary.Positive = unary.Negative
如果要保护自己免受滥用,还必须使此类全局变量不导出。然后,当然,您必须创建导出的函数以公开这些值,例如:
var ( positive = unary{1}
negative = unary{-1}
)
func Positive() unary { return positive }
func Negative() unary { return negative }
然后获取/使用这些值:
p := unary.Positive()
接口
如果打算为“常量”使用接口类型,则必须小心。卡夫·沙赫巴赞的回答中可以看到一个例子。未导出的方法用于防止其他人实现该接口,给您一种幻觉,即其他人确实无法实现该接口:
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
可以嵌入导出的类型,并且可以使用现有值来实现接口(以及未导出的方法),并且我们可以添加导出方法的自己的实现,执行/返回我们想要的任何操作。
看起来是这样的:
type MyUn struct { unary.Unary
}
func (m MyUn) String() string { return "/" }
func (m MyUn) CalExpr() int { return 3 }
测试它:
p := unary.Positivefmt.Printf("%v %d\n", p, p)
p = MyUn{p}
fmt.Printf("%v %d\n", p, p.CalExpr())
输出:
+ 1/ 3
特殊情况
正如沃尔克在评论中所提到的,在特殊情况下,您可以使用
type unary boolconst (
Positive unary = true
Negative unary = false
)
由于类型bool
具有两个可能的值:true
和false
,因此我们已经使用了所有值。因此,没有其他可以“利用”的值来创建我们常量类型的其他值。
但是要知道,只有在常量的数量等于类型的可能值的数量时才能使用此方法,因此该技术的可用性非常有限。
另外请记住,当unary
期望使用的类型,并且有人不小心传递了诸如true
或的无类型常量时,这不能防止这种滥用false
。
以上是 创建一个常量类型并限制类型的值 的全部内容, 来源链接: utcz.com/qa/397299.html