创建一个常量类型并限制类型的值 [英] Creating a Constant Type and Restricting the Type's Values

查看:63
本文介绍了创建一个常量类型并限制类型的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个关于限于某些值的常量类型以及如何在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 type T ("x is assignable to T") in any of these cases:


  • ...

  • x 是无类型的常量可以用 T 类型的值表示。

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

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 structs, 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆