英文:
Type casting issue in go
问题
我该如何重写以下代码?
switch md.(type) {
case *amf0.EcmaArrayType:
ea := md.(*amf0.EcmaArrayType)
for k, v := range (*ea) {
log.Printf("%v = %v\n", k, v)
}
if width == 0 {width = uint16((*ea)["width"].(amf0.NumberType))}
if height == 0 {height = uint16((*ea)["height"].(amf0.NumberType))}
case *amf0.ObjectType:
ea := md.(*amf0.ObjectType)
for k, v := range (*ea) {
log.Printf("%v = %v\n", k, v)
}
if width == 0 {width = uint16((*ea)["width"].(amf0.NumberType))}
if height == 0 {height = uint16((*ea)["height"].(amf0.NumberType))}
}
它有两个完全重复的块,用于不同的类型。如果我在switch语句之前声明了var ea interface{}
,由于编译错误,我无法调用range (*ea)
。
英文:
How I can rewrite following code?
switch md.(type) {
case *amf0.EcmaArrayType:
ea := md.(*amf0.EcmaArrayType)
for k, v := range (*ea) {
log.Printf("%v = %v\n", k, v)
}
if width == 0 {width = uint16((*ea)["width"].(amf0.NumberType))}
if height == 0 {height = uint16((*ea)["height"].(amf0.NumberType))}
case *amf0.ObjectType:
ea := md.(*amf0.ObjectType)
for k, v := range (*ea) {
log.Printf("%v = %v\n", k, v)
}
if width == 0 {width = uint16((*ea)["width"].(amf0.NumberType))}
if height == 0 {height = uint16((*ea)["height"].(amf0.NumberType))}
}
It has two completely duplicated blocks for different types. If I declared var ea interface{}
above switch state, I cannot call range (*ea)
due to compilation error.
答案1
得分: 2
看起来这两种类型都有底层类型map[string]something
,其中"something"具有具体类型amf0.NumberType
。你所做的每个操作都可以使用反射来模拟。
switch md.(type) {
case *amf0.EcmaArrayType, *amf0.ObjectType:
m := reflect.Indirect(reflect.ValueOf(md))
for _, key := range m.MapKeys() {
k, v := key.Interface(), m.MapIndex(key).Interface()
log.Printf("%v = %v\n", k, v)
}
if width == 0 {
w := m.MapIndex(reflect.ValueOf("width"))
width = uint16(w.Interface().(amf0.NumberType))
}
if height == 0 {
h := m.MapIndex(reflect.ValueOf("height"))
height = uint16(h.Interface().(amf0.NumberType))
}
}
然而,在你的第一个示例中做这样的事情并不罕见。有时候反射无法满足需求。在这些情况下,我有一些建议给你的类型切换。不要使用switch md.(type)
,而是使用switch ea := md.(type)
。这样可以避免像ea := md.(*amf0.EcmaArrayType)
这样的重复行。
DRY(Don't Repeat Yourself)的代码更易于处理。它使得更改更快捷,且更不容易出错。然而,当所有重复的代码都集中在一个地方(比如类型切换)时,出错的机会很小。虽然更改仍然需要更多时间,但它并不像在项目中重复代码那样糟糕。不要像对待其他重复代码那样害怕大量重复的类型切换。
英文:
It looks like both of these types have the underlying type map[string]something
where "something" has a concrete type of amf0.NumberType
. Every operation you did could be emulated using reflection.
switch md.(type) {
case *amf0.EcmaArrayType, *amf0.ObjectType:
m := reflect.Indirect(reflect.ValueOf(md))
for _, key := range m.MapKeys() {
k, v := key.Interface(), m.MapIndex(key).Interface()
log.Printf("%v = %v\n", k, v)
}
if width == 0 {
w := m.MapIndex(reflect.ValueOf("width"))
width = uint16(w.Interface().(amf0.NumberType))
}
if height == 0 {
h := m.MapIndex(reflect.ValueOf("height"))
height = uint16(h.Interface().(amf0.NumberType))
}
}
It is however not uncommon to do what you did in your first example. There are times when reflect won't cut it. In those cases, I have some advice for your type switch. Instead of switch md.(type)
do switch ea := md.(type)
. That would allow you to remove lines like ea := md.(*amf0.EcmaArrayType)
.
DRY code is nicer to work with. It makes making changes quicker and is less prone to bugs. However, when all your duplicated code is in one place (like a type switch), the chances of bugs are slim. It still takes longer to make changes, but it is not nearly as bad as duplicated code throughout your project. Don't fear heavily duplicated type switches like you would other duplicated code.
答案2
得分: 1
在这个特定的情况下,我可能会给未来的维护者添加注释,指出重复的代码,或者我可能会按照以下方式删除它。 (playground:http://play.golang.org/p/Vc9pOZSNoW)
package main
import "log"
// 从amf0包中复制
type NumberType float64
type StringType string
type _Object map[StringType]interface{}
type ObjectType _Object
type EcmaArrayType _Object
// 测试参数。注释掉其中一个
// var md interface{} = &ObjectType{"height": NumberType(3), "width": NumberType(5)}
var md interface{} = &EcmaArrayType{"height": NumberType(5), "width": NumberType(7)}
func main() {
var width, height uint16
ea, ok := md.(*ObjectType)
if !ok {
if et, ok := md.(*EcmaArrayType); ok {
ea = (*ObjectType)(et)
}
}
if ea != nil {
for k, v := range *ea {
log.Printf("%v = %v\n", k, v)
}
if width == 0 {
width = uint16((*ea)["width"].(NumberType))
}
if height == 0 {
height = uint16((*ea)["height"].(NumberType))
}
}
}
你原来的重复代码只是重复的源代码;由于处理的是不同的类型,编译后的代码是不同的。幸运的是,ObjectType情况下的编译代码可以通过简单的类型转换 ea = (*ObjectType)(et) 来处理EcmaArrayType情况。
英文:
In this specific case, I might just add comments to future maintainers pointing out the duplicate code, or I might remove it as follows. (playground: http://play.golang.org/p/Vc9pOZSNoW)
package main
import "log"
// copied from package amf0
type NumberType float64
type StringType string
type _Object map[StringType]interface{}
type ObjectType _Object
type EcmaArrayType _Object
// test parameter. comment out one or the other
// var md interface{} = &ObjectType{"height": NumberType(3), "width": NumberType(5)}
var md interface{} = &EcmaArrayType{"height": NumberType(5), "width": NumberType(7)}
func main() {
var width, height uint16
ea, ok := md.(*ObjectType)
if !ok {
if et, ok := md.(*EcmaArrayType); ok {
ea = (*ObjectType)(et)
}
}
if ea != nil {
for k, v := range *ea {
log.Printf("%v = %v\n", k, v)
}
if width == 0 {
width = uint16((*ea)["width"].(NumberType))
}
if height == 0 {
height = uint16((*ea)["height"].(NumberType))
}
}
}
The duplicated code in your original was only duplicated source code; since it was handling different types, the compiled code was different. Fortunately though, the compiled code of the ObjectType case can easily handle the EcmaArrayType case with the simple type conversion of ea = (*ObjectType)(et).
答案3
得分: 0
在调用range之前使用类型转换,例如:
range ((*你想要的类型)(*ea))
将你想要的类型
替换为你实际的类型进行类型转换。
英文:
Use the type casting before calling range e.g.
range ((* your_desired_type)(*ea))
Replace the your_desired_type
with your actual type for type casting.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论