当前位置: 首页 > 面试题库 >

创建一个常量类型并限制类型的值

关玄裳
2023-03-14
问题内容

我有一个关于将常量类型限制为某些值的问题,以及如何在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该新值的int值不同于1-1。请参阅以下示例:

p := unary.Positive
fmt.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.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 }

请注意,此解决方案仍然存在“缺陷”:它使用导出的全局变量,其值可以由其他程序包更改。确实其他软件包不能创建和分配
值,但是它们可以使用现有值来创建和分配 值,例如:

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.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期望使用的类型,并且有人不小心传递了诸如true或的无类型常量时,这不能防止这种滥用false



 类似资料:
  • 我有一个关于常量类型的问题,这些常量被限制在特定的值,以及在GO中如何实现这些。假设我创建了一个类型,它有两个常量值和,我希望限制该类型()的用户创建类型的其他值。是否通过创建包并使值和可见,并使类型仅限于包含包来实现这一点?例如,请参见下面的代码 这是将类型限制为某些常量值的正确方法吗?

  • 问题内容: 说,我有如下通用类型 我想让它只接受特定的类型(字符串/整数/双精度)。我知道有界通配符,但在这里对我没有帮助。在setVar()中,如果类型不是Integer / String等,我可以检查并引发异常。这是最好的方法吗? 在对此类型进行操作时,我遇到同样的问题。根据类型,我想执行不同的操作。继承和有界通配符似乎通常是解决此类问题的方法,但它们是原始包装器。 问题答案: 使用继承: P

  • 问题内容: 我有一个我必须验证问题值的对象,这些对象的某些属性是自定义对象的数组。这样一来,这将涉及到数组的各个元素。为每个元素执行吸气剂,例如: 这是我需要达到的。我已使用带有属性列表的枚举以以下方式提取了数据。 致电: 我将使用数组中不同类型和不同值的多个数组。我想创建一个如下的方法。 这给了我多个错误,这些错误主要与repeatingGrp []有关。数组类型应与枚举名称相同。是否可以创建这

  • 本页包含内容: 自定义原型 实现默认值 支持基本布尔型初始化 支持Bool类型判断 支持兼容各们各派的类型 完善OCBool的布尔基因体系 小伙伴们,Swift中的Bool类型有着非常重要的语法功能,并支撑起了整个Swift体系中的逻辑判断体系,经过老码的研究和学习, Bool类型本身其实是对基础Boolean类型封装,小伙伴们可能咬着手指头问老码,怎么一会Bool类型,一会Boolean类型,其

  • \Vtiful\Kernel\Chart Class Const 常量 图表类型 CHART_NONE 无 CHART_AREA 面积图 CHART_AREA_STACKED 面积图 - 堆积 CHART_AREA_STACKED_PERCENT 面积图 - 堆积百分比 CHART_BAR 条形图 CHART_BAR_STACKED 条形图 - 堆积 CHART_BAR_STACKED_PERC

  • const TYPE_STRING = 0x01; // 字符串 const TYPE_INT = 0x02; // 整型 const TYPE_DOUBLE = 0x04; // 浮点型 const TYPE_TIMESTAMP = 0x08; // 时间戳,可以将 xlsx 文件中的格式化时间字符转为时间戳