Dave C имеет хороший ответ, используя reflect
, и я сравню его с типом по типу кода ниже. Во-первых, делать то, что вы уже делали более сжато, вы можете использовать type switch:
switch i := unk.(type) {
case float64:
return i, nil
case float32:
return float64(i), nil
case int64:
return float64(i), nil
// ...other cases...
default:
return math.NaN(), errors.New("getFloat: unknown value is of incompatible type")
}
case float64:
как ваш if i, ok := unk.(float64); ok { ... }
. Код для этого случая: float64
как i
. Несмотря на отсутствие брекетов, случаи действуют как блоки: тип i
отличается под каждым case
, и нет провала C-стиля.
Кроме того, обратите внимание large int64
s (over 253) will be rounded при преобразовании в float64
, так что если вы думаете о float64
как «универсальный» тип номера, принимать свои ограничения во внимание.
Примером этого является детская площадка в http://play.golang.org/p/EVmv2ibI_j.
Dave C упоминает вас может избегать выписывая отдельные случаи, если вы используете reflect
; его ответ имеет код и даже обрабатывает названные типы и указатели на подходящие типы. Он также упоминает, как обрабатывать строки и типы, конвертируемые в них. После выбора наивных вариантов тестирования:
- Передача
reflect
версии a int дает мне около 3 миллионов конверсий в секунду; накладные расходы не заметны, если вы не конвертируете миллионы предметов.
- Вы можете написать переключатель для обработки некоторых распространенных типов, а затем вернуться к
reflect
; это немного быстрее для обычных типов (9 м/с), но сохраняет гибкость кода reflect
.
Код on the Playground и ниже:
package main
/* To actually run the timings, you need to run this from your machine, not the Playground */
import (
"errors"
"fmt"
"math"
"reflect"
"runtime"
"strconv"
"time"
)
var floatType = reflect.TypeOf(float64(0))
var stringType = reflect.TypeOf("")
func getFloat(unk interface{}) (float64, error) {
switch i := unk.(type) {
case float64:
return i, nil
case float32:
return float64(i), nil
case int64:
return float64(i), nil
case int32:
return float64(i), nil
case int:
return float64(i), nil
case uint64:
return float64(i), nil
case uint32:
return float64(i), nil
case uint:
return float64(i), nil
case string:
return strconv.ParseFloat(i, 64)
default:
v := reflect.ValueOf(unk)
v = reflect.Indirect(v)
if v.Type().ConvertibleTo(floatType) {
fv := v.Convert(floatType)
return fv.Float(), nil
} else if v.Type().ConvertibleTo(stringType) {
sv := v.Convert(stringType)
s := sv.String()
return strconv.ParseFloat(s, 64)
} else {
return math.NaN(), fmt.Errorf("Can't convert %v to float64", v.Type())
}
}
}
func getFloatReflectOnly(unk interface{}) (float64, error) {
v := reflect.ValueOf(unk)
v = reflect.Indirect(v)
if !v.Type().ConvertibleTo(floatType) {
return math.NaN(), fmt.Errorf("cannot convert %v to float64", v.Type())
}
fv := v.Convert(floatType)
return fv.Float(), nil
}
var errUnexpectedType = errors.New("Non-numeric type could not be converted to float")
func getFloatSwitchOnly(unk interface{}) (float64, error) {
switch i := unk.(type) {
case float64:
return i, nil
case float32:
return float64(i), nil
case int64:
return float64(i), nil
case int32:
return float64(i), nil
case int:
return float64(i), nil
case uint64:
return float64(i), nil
case uint32:
return float64(i), nil
case uint:
return float64(i), nil
default:
return math.NaN(), errUnexpectedType
}
}
func main() {
var m1, m2 runtime.MemStats
runtime.ReadMemStats(&m1)
start := time.Now()
for i := 0; i < 1e6; i++ {
getFloatReflectOnly(37)
}
fmt.Println("Reflect-only, 1e6 runs:")
fmt.Println("Wall time:", time.Now().Sub(start))
runtime.ReadMemStats(&m2)
fmt.Println("Bytes allocated:", m2.TotalAlloc-m1.TotalAlloc)
runtime.ReadMemStats(&m1)
start = time.Now()
for i := 0; i < 1e6; i++ {
getFloat(37)
}
fmt.Println("\nReflect-and-switch, 1e6 runs:")
fmt.Println("Wall time:", time.Since(start))
runtime.ReadMemStats(&m2)
fmt.Println("Bytes allocated:", m2.TotalAlloc-m1.TotalAlloc)
runtime.ReadMemStats(&m1)
start = time.Now()
for i := 0; i < 1e6; i++ {
getFloatSwitchOnly(37)
}
fmt.Println("\nSwitch only, 1e6 runs:")
fmt.Println("Wall time:", time.Since(start))
runtime.ReadMemStats(&m2)
fmt.Println("Bytes allocated:", m2.TotalAlloc-m1.TotalAlloc)
}
/*
Reflect-only, 1e6 runs:
Wall time: 297.335642ms
Bytes allocated: 16006648
Reflect-and-switch, 1e6 runs:
Wall time: 110.40057ms
Bytes allocated: 8001144
Switch only, 1e6 runs:
Wall time: 15.069157ms
Bytes allocated: 64
*/
Спасибо, я был не зная, что вы можете переключаться с типами – Rhaokiel
У позора не может быть только один случай для вариантов int 'case int, int32, int64: return float64 (t)' –
Различные типы потенциально могут быть скомпилированы для разных машинных кодов, поэтому я могу понять, почему они это сделали. – twotwotwo