将私有的、动态类型从interface{}转换

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

Converting private, dynamic type from interface{}

问题

我正在尝试测试一个 SQL 查询,其中一个参数是 gosnowflake.Array(实质上是一个对切片的包装器),使用 go-sqlmock 包。通常情况下,这样的情况需要我创建一个值转换器,我已经包含了以下代码:

func (opt arrayConverterOption[T]) ConvertValue(v any) (driver.Value, error) {
    casted, ok := v.(*[]T)
    if ok {
        Expect(*casted).Should(HaveLen(len(opt.Expected)))
        for i, c := range *casted {
            Expect(c).Should(Equal(opt.Expected[i]))
        }
    } else {
        fmt.Printf("Type: %T\n", v)
        return v, nil
    }

    return "TEST_RESULT", nil
}

现在,这个函数会为提交给查询的每个参数调用。我使用它来测试切片中的值的正确性,或者如果不是切片,则直接传递参数。我遇到的问题是,当我创建一个 arrayConverterOption[string] 并给它一个 gosnowflake.Array(["A", "B", "C"]) 作为参数时,类型断言失败,因为 gosnowflake.Array 返回一个内部动态类型 *stringArray,它被定义为 *[]string

所以你可以看到我的困境。一方面,我不能转换 v,因为它是一个 interface{},我也不能给 v 起别名,因为内部类型不是 *[]string,而是 *stringArray。那么,在这种情况下我该怎么办呢?

英文:

I'm trying to test around an SQL query wherein one of the arguments is a gosnowflake.Array (essentially a wrapper to a slice) using the go-sqlmock package. Normally, something like this requires me to create a value converter, which I have included:

func (opt arrayConverterOption[T]) ConvertValue(v any) (driver.Value, error) {
	casted, ok := v.(*[]T)
	if ok {
		Expect(*casted).Should(HaveLen(len(opt.Expected)))
		for i, c := range *casted {
			Expect(c).Should(Equal(opt.Expected[i]))
		}
	} else {
		fmt.Printf("Type: %T\n", v)
		return v, nil
	}

	return "TEST_RESULT", nil
}

Now, this function is called for every argument submitted to the query. I use it to test the correctness of the values in the slice or pass the argument through if it isn't one. The problem I'm having is that, when I create a arrayConverterOption[string] and give it a gosnowflake.Array(["A", "B", "C"]) as an argument, the type assertion fails because gosnowflake.Array returns an internal dynamic type, *stringArray, which is defined as a *[]string.

So you can see my dilemma here. On the one hand, I can't convert v because it's an interface{} and I can't alias v because the inner type is not *[]string, but *stringArray. So then, what should I do here?

答案1

得分: 0

我没有找到不使用反射的方法来实现这个。然而,通过使用反射,我成功地实现了它:

var casted []T
var ok bool

value := reflect.ValueOf(v)
if value.Kind() == reflect.Pointer {
	if inner := value.Elem(); inner.Kind() == reflect.Slice {
		r := inner.Convert(reflect.TypeOf([]T{})).Interface()
		casted, ok = r.([]T)
	}
}

所以,这段代码专门检查指向切片的任何内容,这是我的动态类型。然后,它使用反射将内部对象转换为我期望的切片类型。之后,我调用Interface()方法来从反射值中获取interface{},然后将其转换为[]T类型。这样就成功了。如果失败了,那么我就不是在处理这些动态类型的切片,我可以按照正常的方式处理该类型。

英文:

I didn't find a way to do this without resulting to reflection. However, with reflction I did manage it:

var casted []T
var ok bool

value := reflect.ValueOf(v)
if value.Kind() == reflect.Pointer {
	if inner := value.Elem(); inner.Kind() == reflect.Slice {
		r := inner.Convert(reflect.TypeOf([]T{})).Interface()
		casted, ok = r.([]T)
	}
}

So, this code checks specifically for anything that is a pointer to a slice, which my dynamic type is. Then it uses reflection to convert the inner object to the slice type I was expecting. After that, I call Interface() on the result to get the interface{} from the reflected value and then cast it to a []T. This succeeds. If it doesn't then I'm not working with one of those dynamically typed slices and I can handle the type normally.

huangapple
  • 本文由 发表于 2022年12月22日 16:18:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/74885801.html
匿名

发表评论

匿名网友

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

确定