Uma olhada em Vá pelos olhos de um desenvolvedor .NET. Semana # 1

Olá pessoal!

Meu nome é Lex e eu sou o apresentador do canal IT Co. no youtube E eu sou um associado de 6 anos de idade. Recentemente, eu queria ir além da minha tecnologia principal (C # /. NET) e entender a essência do paradoxo do Blob . Eu decidi firmemente que me tentaria em outro idioma, e a escolha por acaso recaiu em Go.

Para obter conhecimento de maneira estruturada, inscrevi-me em um curso no mail.ru, que você pode encontrar aqui: https://www.coursera.org/learn/golang-webservices-1 . Na verdade, o que aprendi na primeira semana de treinamento neste curso será discutido mais adiante. Vamos lá!


Vou começar com uma pequena introdução ao idioma. O Go já foi desenvolvido na época dos processadores com vários núcleos (2007-2009), então tudo é muito bom aqui com o trabalho paralelo entre os núcleos. Além disso, o idioma funciona muito bem com solicitações competitivas (simultâneas). Em geral, uma descoberta para todos os tipos de serviços da web e sistemas da web carregados. Apenas o que é necessário para mim, porque tenho desenvolvido API da Web (Web API) nos últimos anos.

Iniciar


Para trabalhar com o idioma, basta instalar o pacote de software “Go tools” de 118mb de tamanho e você pode começar a codificar.

Os scripts têm a extensão * .go e são executados pelo comando go na linha de comando (sou adepto da linha de comando do Windows). O comando gofmt coloca todos os espaços de indentação e produz doces a partir do arquivo (embelezador de código fora da caixa). Como na minha sub-rede favorita, no Go, a execução do programa começa com o método principal .
Um recurso interessante chamou minha atenção imediatamente - você não pode colocar um ponto e vírgula no final da linha :)

Go é outra linguagem (juntamente com python, c #, php, js, ts e outras), conveniente para trabalhar no VSCode. O próprio VSCode oferece absolutamente todos os pacotes e dependências necessários à medida que o código é escrito. Ao salvar o IDE, execute o gofmt para você e torne o código ainda mais bonito.

Variáveis


Tudo é familiar aqui: existem muitos tipos diferentes, existem valores padrão. De incomum para um doador, a palavra-chave var significa que a variável irá além, e o tipo da variável pode ser declarado ou ainda não após o nome da variável. Invulgarmente.

Em geral, existem muitas maneiras de declarar e inicializar variáveis. Você pode até atribuir valores a várias variáveis, separadas por vírgulas, na mesma linha. E, a propósito, não há modelos implícitos.

O And Go também possui um operador " : = ", que permite declarar e inicializar imediatamente novas (e quase sempre apenas novas) variáveis. Olá Pascal;)

Visualização de registro:

perem1, perem2 := 2, 3 
cria duas novas variáveis ​​ou cria uma delas e a segunda simplesmente atribui um novo valor. Isso significa que, se as duas variáveis ​​já existirem, isso não funcionará e você receberá um erro, elas dizem usar a atribuição usual. Operador : = deve criar algo :)

Há também uma falsificação engraçada - o caractere sublinhado "_". Significa a ausência de uma variável, algo parecido com isso :) (nos detalhes, como se viu, também é: https://docs.microsoft.com/en-us/dotnet/csharp/discards )

imagem

Momento divertido com strings: a contagem padrão do comprimento de uma string len (str) conta bytes e, em UTF8, um caractere pode pesar mais. Para contar os caracteres chamados runas , você precisa usar o método utf8.RuneCountInString (str) do pacote “utf8”. E, no entanto - não alteramos as linhas (como no seu favorito).

Constantes


As constantes podem ser declaradas em lotes. As constantes têm um tipo interessante, ou algo assim - iota . Vou chamá-lo de Jopta. O Jopta permite fazer algumas constantes com base em outras, como um iterador para constantes (algo que lembra o rendimento de uma sub-rede).

E, no entanto, as constantes não podem ser digitadas. Para essas constantes, o compilador decide que tipo elas são no momento certo. Convenientemente, o que já está lá.



Ponteiros


Sim sim Há dicas aqui, mas não parece muito assustador. Eles dizem que não estão numerados e não é possível obter o próximo ponteiro adicionando um par ao ponteiro. Aqui é como uma variável que armazena um link para outra variável (até agora, de alguma forma, eu a entendi).

O ponteiro é especificado por meio de um ampersante ou do novo método ( tipo) (para obter um ponteiro para um tipo, não para uma variável):



Se você acessar o ponteiro diretamente, o link será alterado. E se através do operador " * ", o valor da variável que está por trás do ponteiro (para o qual aponta) é alterado. Em geral, ainda não está muito claro, mas, dizem, será mais claro.

Matrizes


O tamanho da matriz faz parte do tipo de dados! Portanto, matrizes de diferentes dimensões são de fato tipos diferentes de dados e não são compatíveis.



Mas o que para matrizes é necessário se elas são tão secas e imutáveis ​​(elas não podem ser alteradas em tempo de execução). Portanto, criamos essas fatias (fatias) com base em matrizes.



Uma fatia tem um comprimento e capacidade . Isso me lembrou um pouco do tipo de dados nvarchar do SQL. I.e. você pode alocar espaço de memória (alocação de memória) para suas matrizes, mas colocar matrizes de qualquer tamanho (capacidade). Em suma, a coisa é interessante, você precisa se acostumar. Para uso conveniente de fatias, existem todos os tipos de métodos, como make (), append (), len (), cap () e, com certeza, outros. De uma fatia (fatia), você pode obter um subfatia (fatia de fatia). E, a propósito, append () aumenta a capacidade da fatia.

Se a sub-fatia for igual à fatia, na verdade eles se referirão a um pedaço de memória. E, portanto, uma alteração no valor em uma fatia levará a uma alteração na segunda fatia. Mas se um deles for expandido através de append () , um novo pedaço de memória será alocado.
Em suma, tudo é sério com a memória :)

Mapa, tabela de hash, matriz associativa


Tudo isso é o mesmo. Vá a tabelas de hash para pesquisas rápidas de teclas. Inicializado da seguinte maneira:



Você pode criar subtabelas (Mapa), etc. (qualquer nível de aninhamento, mais ou menos). Há um valor padrão que retorna em chaves inexistentes. É retirado do tipo de chave (para bool, depois false). Para saber se uma variável retornou por padrão, ou seja, que a chave está faltando - use a chave para a existência da chave (coisa interessante):



Estruturas de controle


Existe um if-else. Um pouco mais interessante que nos sharps, porque você pode usar as condições de inicialização para if.

Há um caso de swith. As quebras não precisam ser definidas, e isso é uma diferença. Inversão da lógica) Se você deseja que a seguinte condição também seja verificada, é necessário escrever uma explicação .

Existe para. E tudo com ciclos.



Com uma fatia iterativa mais divertida (operador de intervalo):



E mesmo pelo mapa de hash, você pode iterar (embora a sequência geralmente seja diferente, porque o mapa não é direcional):



Para o tipo string, o intervalo percorre runas, não bytes.

Funções


Declarado através da palavra-chave func . Além disso, a inversão da lógica é novamente comparada à sub-rede - os parâmetros de entrada são indicados primeiro e depois o tipo do valor de retorno (você pode nomeá-lo imediatamente).

As funções podem retornar vários resultados , assim como as tuplas em uma sub - rede .

Os valores nomeados que a função retorna são inicializados por padrão com valores padrão para o tipo.

E, no entanto, você pode criar funções variáveis que tenham um número ilimitado de parâmetros de entrada do mesmo tipo (como param na sub-rede). E então um tipo específico de fatia chega à entrada da função.

As funções podem ter nomes ou podem não ter um nome - anônimo . E eles podem ser chamados, podem ser atribuídos a variáveis. Parece JavaScript. Você pode até criar tipos com base em funções! Funções podem ser passadas como parâmetros (ler delegados da sub-rede)

Há até um fechamento (uuuh, assustador)! Você pode acessar variáveis ​​da função externa (leia-se, na função pai). Estas são as tortas.

Você pode declarar a execução adiada de funções . Através da palavra-chave adiar, isso é feito. I.e. as funções diferidas são executadas no final da função em que são declaradas, na ordem inversa da declaração dessas funções diferidas. Aqui. Além disso, a inicialização dos argumentos das funções adiadas ocorre quando o bloco adiado é declarado . Isso é importante, especialmente se outra função for tomada como argumento - ela será executada muito mais cedo do que o esperado!

PÂNICO! Go tem essa função - panic () . É como jogar , mas pior. Esta função interrompe a execução do programa. Mas esse mesmo pânico pode ser processado por adiamento, pois é executado de qualquer forma no final da função, mesmo após um pânico. E, no entanto - em pânico, isso não é uma tentativa de captura. Isso é pior!

Estruturas (objetos próximos)


Diz-se que Go não é exatamente uma linguagem de paradigma OOP. Mas existem coisas como estruturas . Se você é do mundo das sub-redes, sabe que também temos estruturas - esses são mapeamentos de objetos que podem ser armazenados na pilha (tipos de valor). No Go, a estrutura é a única maneira de fazer algo como um objeto. Ainda não posso falar sobre os tipos de valor, mas a natureza da interação com as estruturas é muito semelhante à detalhada.

Cada estrutura é essencialmente um tipo separado e pode ter um conjunto de propriedades de tipos específicos (incluindo o tipo de outra estrutura ou tipo de função):



Além disso, um certo mecanismo de herança direta de objetos da sub-rede é visto aqui. Na figura acima, a estrutura da conta está aninhada com a estrutura da pessoa . Isso significa que todos os campos de Pessoa estarão disponíveis em uma variável do tipo Conta . Se os nomes das propriedades coincidirem (no exemplo acima Id, Nome ), não haverá conflito, mas o valor de um campo superior será utilizado:



Métodos


Sim, não há apenas a semelhança de objetos, mas também métodos. Quase OOP :) Um método é uma função vinculada a um tipo específico (por exemplo, a uma estrutura). Um método difere de uma função em uma declaração: após a palavra-chave func , entre colchetes você precisa especificar o tipo ao qual esse método pertence e a função de passar uma variável desse tipo. Qual é o papel? Se você colocar um asterisco na frente do tipo, a variável será passada por referência (lembre-se dos ponteiros, também estava lá) e, como resultado, dentro do método, trabalharemos com um tipo específico, e não uma cópia dele.



Da figura acima, podemos concluir que UpdateName não faz sentido, porque altera a cópia da estrutura, e não o original. E esta cópia não retorna. Enquanto SetName mudará a estrutura original (graças ao asterisco e passando por referência).

Os métodos nas estruturas são herdados (e de acordo com as regras de herança de propriedade), ou seja, a estrutura pai tem acesso a todos os métodos aninhados em suas estruturas.

Outros tipos podem ter métodos, não apenas estruturas. Isso pode parecer métodos de extensão da sub-rede, mas não. Os métodos no Go podem ser criados apenas para tipos locais, ou seja, tipos que são declarados neste pacote (sobre pacotes um pouco mais).

Pacotes, escopo, namespaces


Só agora eu percebi que o Go não tem NEIMSPACE! Geralmente! Percebi isso quando, após a compilação, ocorreu um erro, eles disseram que em uma pasta eu tenho dois arquivos com o método principal. Acontece que, na fase de compilação, tudo do papai parece estar colado em uma única tela! Uma pasta de fato é um espaço para nome. Isso é tão mágico, camaradas :)

A propósito, aqui está o que a doca diz:



Nota para si mesmo: fumar uma doca
E aqui está outro pequeno artigo no tópico: https://www.callicoder.com/golang-packages/#the-main-package-and-main-function

Então, eles dizem, a estrutura básica de pastas se parece com:


bin - binários compilados
pkg - arquivos de objetos temporários
src - fontes
Pacotes referem-se ao nome da pasta na qual os arquivos do pacote estão localizados. Sim, um pacote pode ter muitos arquivos importados um para o outro.

Outra regra interessante: funções, propriedades, variáveis ​​e constantes começando com uma letra maiúscula podem ser usadas fora do pacote , ou seja, ser importado . Tudo com uma letra minúscula é usado apenas dentro da embalagem. Um análogo dos modificadores de acesso da sub-rede.

A palavra-chave import funciona dentro de um arquivo, não em um pacote.

Interfaces


As interfaces aqui são interessantes. Não precisamos herdar interfaces de nossas estruturas. É suficiente que a estrutura, que vem como um parâmetro de entrada para um determinado método que usa um tipo de interface, implemente os métodos dessa interface. E não há problemas. I.e. métodos de interface não precisam ser necessariamente implementados pela estrutura. Mas a estrutura deve conter a implementação de todos os métodos necessários em uma função específica, em que o tipo de interface é usado como um parâmetro de entrada.

Acontece que, ao contrário do Go, a interface é uma característica da função em que é usada, e não uma característica de uma estrutura específica (objeto).

Se for ainda mais simples, a lógica é a seguinte: você não precisa ser capaz de grasnar para ser um pato. Se você sabe grasnar, provavelmente você é um pato :)
E, a propósito, se sua estrutura implementa todos os métodos de alguma interface, essa estrutura pode ser atribuída como os valores da variável da interface implementada . Algo assim :)

Tipo caixa do interruptor


O Go possui uma caixa de comutação especial que pode funcionar dependendo do tipo de entrada. Coisa legal:



A propósito, é assim que você pode converter um tipo para outro (por exemplo, um tipo de interface em um tipo de estrutura para obter campos de estrutura que não são acessíveis a partir da interface):



ok - sinal booleano de que a conversão foi bem-sucedida. Já encontramos os sinais :)

A interface vazia é a besta no mundo de Go. Pode levar qualquer tipo. Algo como dinâmico ou Objeto . Por exemplo, é usado em fmt.Println () e funções semelhantes que usam uma interface vazia na implementação.

As interfaces podem ser construídas umas nas outras e, assim, criar a composição de interfaces (o que é dito na letra I (segregação de interfaces) dos princípios do SOLID )

Testes


No Go, todas as funções de teste começam com a palavra Teste e a entrada do teste do módulo de teste é aceita. Os arquivos são nomeados pelo nome do arquivo de teste + palavra _test ( main_test.go - testa o arquivo main.go ). Além disso, os testes e os arquivos de teste estão em um pacote!



Epílogo


Isso é tudo! Agradecemos sua atenção e estou pronto para discutir questões nos comentários. Encontro você nas seguintes notas das minhas aulas!

PS Você pode olhar para todo o meu código nesta semana de treinamento no github

Source: https://habr.com/ru/post/pt455830/


All Articles