GO Language Interview Essence: ¿Cuál es la diferencia entre conversión de tipos y aserción?

Sabemos que la conversión de tipos implícita no está permitida en el lenguaje Go, es decir, =no se permite que aparezcan variables con diferentes tipos en ambos lados.

类型转换, 类型断言la esencia es convertir un tipo en otro tipo. La diferencia es que las afirmaciones de tipo operan sobre variables de interfaz.

conversión de tipo

Para 类型转换, los dos tipos antes y después de la conversión deben ser compatibles entre sí. La sintaxis para la conversión de tipos es:

<tipo de resultado> := <tipo de destino> ( <expresión> )

package main

import "fmt"

func main() {
	var i int = 9

	var f float64
	f = float64(i)
	fmt.Printf("%T, %v\n", f, f)

	f = 10.8
	a := int(f)
	fmt.Printf("%T, %v\n", a, a)

	// s := []int(i)
}

En el código anterior, definí una variable de inttipo y float64tipo e intenté convertirla entre sí antes que ellas, pero el resultado fue exitoso: inttipo y float64son compatibles entre sí.

Si descomento la última línea de código, el compilador informará un error de incompatibilidad de tipos:

cannot convert i (type int) to type []int

afirmación

Como se mencionó anteriormente, debido a que la interfaz vacía interface{}no define ninguna función, todos los tipos en Go implementan la interfaz vacía. Cuando el parámetro formal de una función es interface{}, entonces en la función, se debe hacer una afirmación sobre el parámetro formal para obtener su tipo verdadero.

La sintaxis de las afirmaciones es:

<valor del tipo de destino>, <parámetro booleano> := <expresión>.(tipo de destino) // Aserción de tipo seguro <
valor del tipo de destino> := <expresión>.(tipo de destino) //Aserción de tipo inseguro

La conversión de tipos es algo similar a la aserción de tipo, la diferencia es que la aserción de tipo opera en la interfaz.

Veamos un breve ejemplo:

package main

import "fmt"

type Student struct {
	Name string
	Age int
}

func main() {
	var i interface{} = new(Student)
	s := i.(Student)
	
	fmt.Println(s)
}

Ejecutarlo:

panic: interface conversion: interface {
    
    } is *main.Student, not main.Student

Directamente panic, esto se debe a ique es *Studentde tipo, no Studentde tipo, y la aserción falla. Esto sucedió directamente panic. Es posible que el código en línea no sea adecuado para esto. Puede utilizar la sintaxis de "aserción de seguridad":

func main() {
	var i interface{} = new(Student)
	s, ok := i.(Student)
	if ok {
		fmt.Println(s)
	}
}

De esta manera, incluso si la afirmación falla, no lo hará panic.

En realidad, existe otra forma de aserción, que se utiliza para switchdeterminar el tipo de interfaz mediante declaraciones. Cada uno caseserá considerado secuencialmente. Cuando se golpea a , casese ejecutarán caselas declaraciones en , por lo que caseel orden de las declaraciones es muy importante, porque puede haber varias casecoincidencias.

El ejemplo de código es el siguiente:

func main() {
	//var i interface{} = new(Student)
	//var i interface{} = (*Student)(nil)
	var i interface{}

	fmt.Printf("%p %v\n", &i, i)

	judge(i)
}

func judge(v interface{}) {
	fmt.Printf("%p %v\n", &v, v)

	switch v := v.(type) {
	case nil:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("nil type[%T] %v\n", v, v)

	case Student:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("Student type[%T] %v\n", v, v)

	case *Student:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("*Student type[%T] %v\n", v, v)

	default:
		fmt.Printf("%p %v\n", &v, v)
		fmt.Printf("unknow\n")
	}
}

type Student struct {
	Name string
	Age int
}

mainHay tres líneas diferentes de declaraciones en la función. Ejecute una línea cada vez y comente las otras dos líneas para obtener tres conjuntos de resultados de ejecución:

// --- var i interface{
    
    } = new(Student)
0xc4200701b0 [Name: ], [Age: 0]
0xc4200701d0 [Name: ], [Age: 0]
0xc420080020 [Name: ], [Age: 0]
*Student type[*main.Student] [Name: ], [Age: 0]

// --- var i interface{
    
    } = (*Student)(nil)
0xc42000e1d0 <nil>
0xc42000e1f0 <nil>
0xc42000c030 <nil>
*Student type[*main.Student] <nil>

// --- var i interface{
    
    }
0xc42000e1d0 <nil>
0xc42000e1e0 <nil>
0xc42000e1f0 <nil>
nil type[<nil>] <nil>

Para la primera línea de declaración:

var i interface{} = new(Student)

iEs un *Studenttipo que coincide con el tercer caso. A juzgar por las tres direcciones impresas, las variables en estos tres lugares son en realidad diferentes. mainHay una variable local en la función ; ial llamar a la función, en realidad se copia una copia de los parámetros, por lo que hay otra variable en la función v, que es iuna copia de; después de la afirmación, se genera una nueva copia. Entonces las direcciones de las tres variables finalmente impresas son diferentes.

Para la segunda línea de declaraciones:

var i interface{} = (*Student)(nil)

Lo que quiero explicar aquí es que iel tipo dinámico aquí es (*Student), los datos son nil, su tipo no nil, y cuando se nilcompara con, el resultado obtenido también es false.

La última línea de declaración:

var i interface{}

Esta vez ies nilel tipo.

[Extensión 1]
fmt.PrintlnLos parámetros de la función son interface. Para los tipos integrados, la función utilizará un método exhaustivo para descubrir su tipo verdadero y luego lo convertirá en una cadena para imprimir. Para un tipo personalizado, primero determine si el tipo implementa String()el método. Si lo hace, el resultado del método se imprimirá directamente String(); de ​​lo contrario, los miembros del objeto se atravesarán mediante reflexión para imprimir.

Veamos nuevamente un ejemplo breve, es relativamente simple, no te pongas nervioso:

package main

import "fmt"

type Student struct {
	Name string
	Age int
}

func main() {
	var s = Student{
		Name: "qcrao",
		Age: 18,
	}

	fmt.Println(s)
}

Debido a que Studentla estructura no implementa String()el método, fmt.Printlnse utilizará la reflexión para imprimir las variables miembro una por una:

{
    
    qcrao 18}

Agregue una String()implementación del método:

func (s Student) String() string {
	return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}

Resultado de impresión:

[Name: qcrao], [Age: 18]

Imprima según nuestro método personalizado.

[Extensión 2]
Respecto al ejemplo anterior, si lo cambia:

func (s *Student) String() string {
	return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}

Tenga en cuenta que los tipos de receptor de las dos funciones son diferentes. Ahora Studentla estructura solo tiene una función con el tipo de receptor. Imprima el resultado 指针类型:String()

{
    
    qcrao 18}

¿Por qué?

Un tipo tiene sólo métodos cuyos Treceptores son y ; un tipo tiene métodos cuyos receptores son y . Sintácticamente, los métodos que se pueden ajustar directamente son solo azúcar sintáctico para.T*TT*TT*TGo

Por lo tanto, cuando Studentla estructura define String()un método cuyo tipo de receptor es un tipo de valor, pase

fmt.Println(s)
fmt.Println(&s)

Todo se puede imprimir en formatos personalizados.

Si Studentla estructura define String()un método cuyo tipo de receptor es un tipo de puntero, solo se puede pasar

fmt.Println(&s)

para imprimir en un formato personalizado.

Supongo que te gusta

Origin blog.csdn.net/zy_dreamer/article/details/132795760
Recomendado
Clasificación