在Go语言中使用反射检查兼容类型。

huangapple go评论82阅读模式
英文:

Checking for compatible types using reflection in Go

问题

尽管我知道在Go语言中恐慌可能不是惯用的做法,但我想测试一下,以确保在某些条件下函数会引发恐慌,而在其他条件下不会引发恐慌。

以下是一个函数的示例:

func PanicOnErr(potentialErr error) {
    if potentialErr != nil {
        panic(potentialErr)
    }
}

以下是用于检查函数是否会引发恐慌的实现:

func InvocationCausedPanic(f interface{}, params ...interface{}) bool {
    // 获取函数的签名。
    reflectedFunc := reflect.ValueOf(f)
    funcType := reflect.TypeOf(f)

    if funcType.NumIn() != len(params) {
        panic("InvocationCausedPanic called with a function and an incorrect number of parameter(s).")
    }

    reflectedParams := make([]reflect.Value, len(params))
    for paramIndex, paramValue := range params {
        expectedType := funcType.In(paramIndex)
        actualType := reflect.TypeOf(paramValue)

        if actualType != expectedType {
            errStr := fmt.Sprintf("InvocationCausedPanic called with a mismatched parameter type [parameter #%v: expected %v; got %v].", paramIndex, expectedType, actualType)
            panic(errStr)
        }

        reflectedParams[paramIndex] = reflect.ValueOf(paramValue)
    }

    return invoke(reflectedFunc, reflectedParams)
}

func invoke(reflectedFunc reflect.Value, reflectedParams []reflect.Value) (panicked bool) {
    defer func() {
        if r := recover(); r != nil {
            panicked = true
        }
    }()

    reflectedFunc.Call(reflectedParams)
    return
}

调用以下任一函数将导致类型检查失败。

InvocationCausedPanic(PanicOnErr, errors.New("Some error."))
InvocationCausedPanic(PanicOnErr, nil)

然而,似乎可以使用nil和通过调用errors.New生成的内容(似乎是*errors.errorString类型)来调用PanicOnErr

因此,是否有一种方法可以检查某个参数的类型是否适合调用某个函数?

虽然我知道可以使用deferrecover更简单地测试函数,但我想知道是否有可能编写一个通用函数,可以接受任何函数和参数,并确定它是否导致了恐慌(假设函数完成)。

相关的Go Playground:
http://play.golang.org/p/qUG7OGuIbD

英文:

Although I am aware that it might not be idiomatic to panic in Go, I would like to test to ensure a function panics under certain conditions and not in others.

An example of the function.

func PanicOnErr(potentialErr error) {
    if potentialErr != nil {
	    panic(potentialErr)
    }
}

The following is an implementation for checking if the function will panic.

func InvocationCausedPanic(f interface{}, params ...interface{}) bool {
	// Obtain the function's signature.
	reflectedFunc := reflect.ValueOf(f)
	funcType := reflect.TypeOf(f)

	if funcType.NumIn() != len(params) {
		panic("InvocationCausedPanic called with a function and an incorrect number of parameter(s).")
	}

	reflectedParams := make([]reflect.Value, len(params))
	for paramIndex, paramValue := range params {
		expectedType := funcType.In(paramIndex)
		actualType := reflect.TypeOf(paramValue)

		if actualType != expectedType {
			errStr := fmt.Sprintf("InvocationCausedPanic called with a mismatched parameter type [parameter #%v: expected %v; got %v].", paramIndex, expectedType, actualType)
			panic(errStr)
		}

		reflectedParams[paramIndex] = reflect.ValueOf(paramValue)
	}

	return invoke(reflectedFunc, reflectedParams)
}

func invoke(reflectedFunc reflect.Value, reflectedParams []reflect.Value) (panicked bool) {
	defer func() {
		if r := recover(); r != nil {
			panicked = true
		}
	}()

	reflectedFunc.Call(reflectedParams)
	return
}

Calling either of the following will cause the type-check to fail.

InvocationCausedPanic(PanicOnErr, errors.New("Some error."))
InvocationCausedPanic(PanicOnErr, nil)

However, it seems possible to call PanicOnErr using both nil and something generate by calling errors.New (seems to be of type *errors.errorString).

As such, is there a way to check if the type of some parameter is suitable for invoking some function?

While I know it is possible to use defer and recover to more simply test the function, I am curious as to whether it is possible to write a general function that can accept any function and parameters and determine whether it resulted in a panic (assuming the function completes).

Relevant Go Playground:
http://play.golang.org/p/qUG7OGuIbD

答案1

得分: 1

使用此函数来确定参数是否兼容:

func compatible(actual, expected reflect.Type) bool {
  if actual == nil {
    k := expected.Kind()
    return k == reflect.Chan || 
           k == reflect.Func || 
           k == reflect.Interface || 
           k == reflect.Map || 
           k == reflect.Ptr || 
           k == reflect.Slice
  }
  return actual.AssignableTo(expected)
}

playground

英文:

Use this function to determine if the parameter is compatible:

func compatible(actual, expected reflect.Type) bool {
  if actual == nil {
	k := expected.Kind()
	return k == reflect.Chan || 
           k == reflect.Func || 
           k == reflect.Interface || 
           k == reflect.Map || 
           k == reflect.Ptr || 
           k == reflect.Slice
  }
  return actual.AssignableTo(expected)
}

<kbd>playground</kbd>

huangapple
  • 本文由 发表于 2014年10月11日 14:46:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/26311918.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定