创建一个常量类型并限制类型的值 [英] Creating a Constant Type and Restricting the Type's Values
问题描述
我有一个关于限于某些值的常量类型以及如何在Go中完成此操作的问题。假设我创建了一个类型 unary
,该类型具有两个常量值 Positive(1)
和 Negative( -1)
,我想限制该类型( unary
)的用户创建其他类型 unary
。通过创建一个包并使值 Positive
和 Negative
可见并使类型一元
是否限于包含包?例如,请参见下面的代码
I have a question about types of constants which are restricted to certain values and how you accomplish that in Go. Say I create a type unary
which has two constant values Positive(1)
and Negative(-1)
and I want to restrict the user of that type (unary
) from creating other values of type unary
. Do I achieve this by creating a package and making the values Positive
and Negative
visible and making the type unary
restricted to the containing package? See code below for example
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
}
将类型限制为某些常量值的正确方法吗?
Is this the correct way to restrict a type to certain constant values?
推荐答案
缺陷
您提出的解决方案以您想要的方式并不安全。可以使用无类型的整数常量来创建一元
的新值,该新值的 int
值与不同1
或 -1
。参见以下示例:
Flaws
Your proposed solution is not safe in a way you want it to be. One can use untyped integer constants to create new values of unary
having a different int
value than 1
or -1
. See this example:
p := unary.Positive
fmt.Printf("%v %d\n", p, p)
p = 3
fmt.Printf("%v %d\n", p, p)
输出将为:
+ 1
- 3
我们可以更改 p
的值以存储 int
值 3
显然不等于正值
或负值
。这是可能的,因为规范:可分配性:
We could change p
's value to store the int
value 3
which is obviously not equal to Positive
nor to Negative
. This is possible because Spec: Assignability:
值
x
是可分配的到 T 类型的变量 rel = nofollow noreferrer>变量(x
可分配给T
)
A value
x
is assignable to a variable of typeT
("x
is assignable toT
") in any of these cases:
- ...
-
x
是无类型的常量可以用T
类型的值表示。
- ...
x
is an untyped constant representable by a value of typeT
.
3
是一个无类型常量,可以用类型为 unary
的值表示,该类型的基础类型为 int
。
3
is an untyped constant, and it is representable by a value of type unary
which has underlying type int
.
在Go中,您不能拥有安全的密码。其中 outsider的常数由于上述原因,软件包无法创建的新值。因为如果要在包中声明常量,则只能使用带有 untyped符号的表达式。版本-也可以在分配中被其他软件包使用(就像在我们的示例中一样)。
In Go you can't have "safe" constants of which "outsider" packages cannot create new values of, for the above mentioned reason. Because if you want to declare constants in your package, you can only use expressions that have "untyped" versions–which may be used by other packages too in assignments (just as in our example).
如果您想完成安全部分,则可以使用未导出的 struct
s,但随后不能在常量声明。
If you want to fulfill the "safe" part, you may use unexported struct
s, but then they cannot be used in 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
中对未导出字段'val'的隐式赋值由于我们现在使用的是 struct
,因此可以通过将值的 string
表示形式添加到结构
:
Note that since we're now using a struct
, we can further simplify our code by adding the string
representation of our values to the 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 }
不e该解决方案仍然具有缺陷:它使用导出的全局变量(更确切地说是包级变量),其值可以被其他包改变。确实其他软件包不能创建和分配 new 值,但是它们可以使用现有值来创建和分配它们,例如:
Note that this solution still has a "flaw": it uses exported global variables (more precisely package-level variables) whose values can be changed by other packages. It's true that other packages cannot create and assign new values, but they can do so with existing values, e.g.:
unary.Positive = unary.Negative
如果要保护自己免受滥用,还必须创建此类全局变量未出口。然后,当然,您必须创建导出的函数以暴露那些值,例如:
If you want to protect yourself from such misuse, you also have to make such global variables unexported. And then of course you have to create exported functions to expose those values, for example:
var (
positive = unary{1}
negative = unary{-1}
)
func Positive() unary { return positive }
func Negative() unary { return negative }
然后获取/使用以下值:
Then acquiring/using the values:
p := unary.Positive()
接口
必须注意如果您打算对常量使用接口类型,则采取此选项。 Kaveh Shahbazian的答案中有一个例子。一种未导出的方法用于防止其他人实现该接口,从而给人一种幻觉,即其他人确实无法实现该接口:
Interface
Care must be taken if you plan to use an interface type for your "constants". An example can be seen in Kaveh Shahbazian's answer. An unexported method is used to prevent others from implementing the interface, giving you the illusion that others truly can't implement it:
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
类型,并且可以使用现有值来实现接口(以及未导出的方法),并且我们可以添加自己的实现
This is not the case however. With a dirty trick, this can be circumvented. The exported Unary
type can be embedded, and an existing value can be used in order to implement the interface (along with the unexported method), and we can add our own implementations of the exported methods, doing / returning whatever we want to.
这是下面的样子:
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
特殊情况
如沃尔克在其评论中所述,在您的特殊情况下,您可以使用
Special case
As Volker mentioned in his comment, in your special case you could just use
type unary bool
const (
Positive unary = true
Negative unary = false
)
由于类型 bool
有两个可能的值: true
和 false
,我们已经全部使用了。因此,没有其他值可以被利用。来创建其他常量类型的值。
As the type bool
has two possible values: true
and false
, and we've used all. So there are no other values that could be "exploited" to create other values of our constant type.
但是要知道,只有在常量的数量等于该类型的可能值的数量时,才可以使用此值。技术非常有限。
But know that this can only be used if the number of constants is equal to the number of possible values of the type, so the usability of this technique is very limited.
还请记住,当期望的类型为 unary
且有人意外地传递了一个无类型的常量,例如 true
或 false
。
Also keep in mind that this does not prevent such misuses when a type of unary
is expected, and someone accidentally passes an untyped constant like true
or false
.
这篇关于创建一个常量类型并限制类型的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!