Golang中如何正确设置interface{}的值

Golang中如何正确设置interface{}的值 我有一个Tag结构体,它包含数据类型、值和字节字段。我使用SetValue和SetBytes函数来设置值和对应的字节。当我设置值时,我也会设置字节,反之亦然。

如果有更好或更符合Go语言习惯的方法,请给予建议。

以下是代码:

//data types
const (
	Bit     uint8 = 1
	Uint8   uint8 = 2
	Int8    uint8 = 3
	//...
)

type Tag struct {
	//...
	DataType     uint8
	bytes        []byte
	value        interface{}
}

func (t *Tag) SetBytes(bytesOfValue []byte) error {
	//try to set value
	var value interface{}
	b := bytes.NewReader(bytesOfValue)
	var err error
	switch t.DataType {
	case Bit:
		var v bool
		err = binary.Read(b, binary.BigEndian, &v)
		value = v
	case Uint8:
		var v uint8
		err = binary.Read(b, binary.BigEndian, &v)
		value = v
	case Int8:
		var v int8
		err = binary.Read(b, binary.BigEndian, &v)
		value = v
		//...
	}
	if err != nil {
		fmt.Println(err.Error())
		return errors.New("reading bytes failed")
	}
	t.value = value
	//set bytes
	t.bytes = bytesOfValue
	return nil
}

func (t *Tag) SetValue(value interface{}) error {
	//try to set bytes
	var v interface{}
	var ok bool
	switch t.DataType {
	case Bit:
		v, ok = value.(bool)
	case Uint8:
		v, ok = value.(uint8)
	case Int8:
		v, ok = value.(int8)
	//...
	}
	if !ok {
		return errors.New("value is not valid")
	}
	b := new(bytes.Buffer)
	err := binary.Write(b, binary.BigEndian, v)
	if err != nil {
		return errors.New("writing bytes failed")
	}
	t.bytes = b.Bytes()
	//set value
	t.value = value
	return nil
}

我像这样设置值和对应的字节:

//create tag
t := Tag{
	DataType: Int8,
}

//set value
err := t.SetValue(int8(-128))
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(t.GetBytes(), t.GetValue())

//set bytes
err = t.SetBytes([]byte{128})
if err != nil {
	fmt.Println(err.Error())
	return
}
fmt.Println(t.GetBytes(), t.GetValue())

这是Playground链接:https://play.golang.org/p/hHpTIw9Fxx5

此致


更多关于Golang中如何正确设置interface{}的值的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

你在开玩笑吗?

如果你对这个主题一无所知,你就不必写!

更多关于Golang中如何正确设置interface{}的值的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,字段名以大写字母开头才能从其他包访问。

很抱歉打扰您,我之前没有阅读第一篇帖子…

这些问题无关紧要。

你曾经使用过 binary 包的 Read 和 Write 函数吗?

你对这个主题有什么建议,还是仅仅在浪费时间?

请为 t 指定一个数据类型。我没有分享完整的代码。有一个 NewTag 函数用于创建标签。 我也编辑了帖子。

测试你的代码。

//set value
t := Tag{}
fmt.Println(t)
t.SetValue(int8(-128))
fmt.Println(t)

{0 [] <nil>}
{0 [] <nil>}

感谢您抽出时间并给予答复,Petrus。

我的问题是关于重复使用 binary.Read 函数,因为它不接受 interface{} 类型的值。

我继续进行开发和测试。由于这是一个底层的设备通信协议,我无法用现代的方法解释所有的设计标准,并且我不想占用您太多时间,我会考虑您的建议。

此致

请为 t 指定一个数据类型。我没有分享完整的代码。有一个 NewTag 函数用于创建标签。

Effective Go

在设计数据结构时,安排每种类型的零值无需进一步初始化即可使用,这是很有帮助的。这意味着数据结构的用户可以创建一个并立即开始工作。

ermanimer:

这是 Go Playground 链接:Go Playground - The Go Programming Language

ermanimer:

有一个用于创建标签的 NewTag 函数。

NewTag 函数在哪里?


//tag struct
type Tag struct {
	DataType uint8
	bytes    []byte
	value    interface{}
}

为什么 Tag.DataType 是导出的?

如果有更好或更地道的实现方式,请指教。

你需要改进你的代码。你也需要提升代码的性能。

Tag 数据结构庞大且低效。重新设计 Tag 结构,将其大小减少 50% 以上。

算法效率低下。将 SetValue 的 CPU 时间减少 90% 或更多。将 SetBytes 的 CPU 时间减少 80% 或更多。将 SetValue 的内存分配减少到零。将 SetBytes 的内存分配减少到一次。

人身攻击(拉丁语为‘针对人’),通常指一种修辞策略,即说话者攻击提出论点者的性格、动机或其他属性,而不是攻击论点本身的实质。

这些问题无关紧要。

你用过 binary 包的 Read 和 Write 函数吗?

你对这个主题有什么建议,还是只是在浪费时间?

Yamil_Bracho:

在 Go 语言中,字段名以大写字母开头才能被其他包访问。

你误解了我的问题。

我知道 Go 为什么导出标识符。

Go 编程语言规范

导出的标识符

标识符可以被导出以允许从另一个包访问它。一个标识符被导出,当且仅当:

  1. 标识符名称的第一个字符是 Unicode 大写字母(Unicode 类别 “Lu”);并且
  2. 该标识符在包块中声明,或者它是一个字段名或方法名。

所有其他标识符均未导出。

petrus:

//tag struct
type Tag struct {
	DataType uint8
	bytes    []byte
	value    interface{}
}

为什么 Tag.DataType 被导出了?

我想知道的是,Tag 结构体的设计者为什么选择导出 Tag.DataType 字段。你知道吗?这是一个好的设计吗?

在Go中正确设置interface{}值的关键是确保类型断言和二进制编解码的一致性。你的实现基本正确,但可以优化类型处理部分。以下是改进后的示例:

// 使用类型转换函数简化代码
func (t *Tag) SetBytes(bytesOfValue []byte) error {
    b := bytes.NewReader(bytesOfValue)
    var value interface{}
    var err error
    
    switch t.DataType {
    case Bit:
        var v bool
        err = binary.Read(b, binary.BigEndian, &v)
        value = v
    case Uint8:
        var v uint8
        err = binary.Read(b, binary.BigEndian, &v)
        value = v
    case Int8:
        var v int8
        err = binary.Read(b, binary.BigEndian, &v)
        value = v
    default:
        return errors.New("unsupported data type")
    }
    
    if err != nil {
        return fmt.Errorf("reading bytes failed: %w", err)
    }
    
    t.value = value
    t.bytes = bytesOfValue
    return nil
}

func (t *Tag) SetValue(value interface{}) error {
    var v interface{}
    
    switch t.DataType {
    case Bit:
        if val, ok := value.(bool); ok {
            v = val
        } else {
            return errors.New("value must be bool for Bit type")
        }
    case Uint8:
        if val, ok := value.(uint8); ok {
            v = val
        } else {
            return errors.New("value must be uint8 for Uint8 type")
        }
    case Int8:
        if val, ok := value.(int8); ok {
            v = val
        } else {
            return errors.New("value must be int8 for Int8 type")
        }
    default:
        return errors.New("unsupported data type")
    }
    
    b := new(bytes.Buffer)
    if err := binary.Write(b, binary.BigEndian, v); err != nil {
        return fmt.Errorf("writing bytes failed: %w", err)
    }
    
    t.bytes = b.Bytes()
    t.value = value
    return nil
}

// 添加获取方法确保类型安全
func (t *Tag) GetValue() interface{} {
    return t.value
}

func (t *Tag) GetBytes() []byte {
    return t.bytes
}

使用示例:

func main() {
    t := &Tag{DataType: Int8}
    
    // 设置值
    if err := t.SetValue(int8(-128)); err != nil {
        panic(err)
    }
    fmt.Printf("Bytes: %v, Value: %v\n", t.GetBytes(), t.GetValue())
    
    // 设置字节
    if err := t.SetBytes([]byte{128}); err != nil {
        panic(err)
    }
    fmt.Printf("Bytes: %v, Value: %v\n", t.GetBytes(), t.GetValue())
    
    // 类型检查示例
    if val, ok := t.GetValue().(int8); ok {
        fmt.Printf("Type safe value: %d\n", val)
    }
}

关键改进:

  1. 使用fmt.Errorf包装错误以保留原始错误信息
  2. 为不支持的DataType添加默认错误处理
  3. 在SetValue中提供更明确的错误信息
  4. 保持二进制编解码的一致性(binary.BigEndian)
  5. 添加类型安全的获取方法示例

这种方法确保了interface{}值的正确设置和类型安全访问,同时保持了字节和值的同步更新。

回到顶部