Desenvolvedor
.NET - Questões de entrevistas
![]() |
Este artigo contém cinco perguntas frequentes que são feitas em entrevistas para desenvolvedores .NET juntamente com a resposta que pode ser considerada correta. |
1- Como implementar a programação assíncrona ?
Resposta suscinta e objetiva:
A programação assíncrona em .NET geralmente é implementada usando as
palavras-chave async e await. sendo é comumente usada para operações
vinculadas a E/S, como acesso a arquivos, consultas de banco de dados e
comunicação de rede.
A programação assíncrona é particularmente útil no
desenvolvimento de aplicativos da Web para evitar o bloqueio da thread principal
durante a execução de operações de E/S síncronas.
Resposta mais detalhada:
Implementar programação é uma prática comum para lidar com operações de entrada e saída (I/O) intensivas, como chamadas de API de rede, acesso a banco de dados, entre outras. Ela permite que o programa continue executando outras tarefas enquanto aguarda a conclusão de operações bloqueantes.
A seguir temos algumas formas de implementar a programação assíncrona:
1- Palavra-chave async e
await:
A forma mais comum e recomendada de implementar operações assíncronas em C# é
usar as palavras-chave async e
await. Ao marcar um
método com async, você pode usar
await para pausar a
execução do método até que a operação assíncrona seja concluída. Isso permite
que o thread seja liberado para realizar outras tarefas enquanto aguarda a
conclusão da operação assíncrona
public async Task |
2- Callbacks:
Antes da introdução de async e
await,
os callbacks eram amplamente usados para lidar com operações assíncronas em
.NET. Você pode usar métodos de retorno de chamada para processar resultados
quando uma operação assíncrona é concluída. No entanto, o código tende a ser
mais complexo e menos legível do que a abordagem
async/await.
Aqui está um exemplo simplificado usando
HttpClient com um
callback:
public void GetDataAsyncWithCallback() { HttpClient client = new HttpClient(); client.GetStringAsync("https://api.example.com/data") .ContinueWith(task => { string result = task.Result; // Processar o resultado aqui }); } |
3- Tarefas assíncronas (Async Tasks):
Antes da introdução do async e
await
no C# 5.0, o modelo de programação assíncrona era baseado em tarefas (Tasks).
Você ainda pode usar o tipo Task diretamente para criar e manipular
operações assíncronas. No entanto, o código tende a ser mais verboso em
comparação com a abordagem async/await. Aqui está um
exemplo:
public Task |
2- Fale o que você conhece sobre injeção de dependência.
Resposta direta:
Injeção de Dependência é a implementação de Inversão de
Controle, onde uma classe não inicializa mais suas dependências, mas as aceita
por meio de construtores ou propriedades.
Três tempos de vida de serviço
comumente usados são Singleton, Scoped e Transient.
• Singleton: A
instância é criada uma vez e usada durante todo o tempo de execução da
aplicação.
• Escopo: Criado um novo para cada escopo, geralmente para cada
solicitação na aplicação web.
• Transiente: Criado toda vez que a dependência
é chamada.
A injeção de dependência aumenta a modularidade e a
flexibilidade e simplifica os testes unitários, substituindo as dependências
reais por modelos.
Resposta suscinta e objetiva:
Injeção de dependência é um padrão de design onde as dependências de uma classe são fornecidas por meio de uma fonte externa, em vez de serem instanciadas dentro da própria classe. Isso promove baixo acoplamento e facilita a substituição de implementações. É comumente implementado através de construtores, propriedades ou métodos. O uso de um container de injeção de dependência simplifica o gerenciamento das dependências em uma aplicação.
Resposta mais detalhada:
A injeção de dependência (IoC - Inversion of Control) é um padrão de design amplamente utilizado na programação orientada a objetos e no desenvolvimento de software em geral. Ele se concentra na separação de responsabilidades, flexibilidade e manutenção do código.
A ideia fundamental por trás da injeção de dependência é remover a responsabilidade de criar e gerenciar as dependências de uma classe, transferindo essa responsabilidade para um componente externo, normalmente conhecido como um container de injeção de dependência.
Em programação, uma dependência é um objeto que outra classe precisa para realizar seu trabalho. Por exemplo, uma classe de serviço pode depender de uma classe de repositório para acessar dados do banco de dados.
A inversão de controle é o princípio fundamental por trás da injeção de dependência. Em vez de uma classe criar diretamente suas dependências, elas são fornecidas (injetadas) por uma fonte externa. Isso inverte o controle do fluxo do programa, dando a um componente externo o controle sobre a criação e o gerenciamento das dependências.
Um Container DI é uma ferramenta que facilita a implementação da injeção de dependência. Ele mantém um registro das dependências disponíveis e de suas configurações. Quando uma classe precisa de uma dependência, o DI Container a fornece. Exemplos populares de DI Containers em .NET incluem o Unity, Autofac, Ninject e o próprio contêiner integrado ao ASP.NET Core.
3- Você poderia explicar os princípios SOLID
Resposta objetiva :
SOLID são cinco princípios de design de software que promovem código limpo e flexível:
SRP: Uma classe deve ter apenas uma razão para mudar.
OCP: As entidades de software devem estar abertas para extensão, mas fechadas para modificação.
LSP: Objetos de um programa devem ser substituíveis por instâncias de seus subtipos.
ISP: Clientes não devem ser forçados a depender de métodos que não usam.
DIP: Módulos de alto nível não devem depender de módulos de baixo nível; ambos devem depender de abstrações.
Esses princípios promovem código mais robusto e de fácil manutenção.
Resposta mais detalhada:
Os princípios SOLID são um conjunto de diretrizes de design de software que visam criar código mais limpo, robusto e flexível. Eles foram introduzidos por Robert C. Martin e são amplamente aceitos na comunidade de desenvolvimento de software.
SOLID é um acrônimo onde cada letra significa o seguinte:
S - Princípio da Responsabilidade Única (Single Responsibility Principle): Uma classe deve ter apenas uma razão para mudar. Em outras palavras, uma classe deve ter uma única responsabilidade e deve ser altamente coesa em torno dessa responsabilidade. Isso promove código mais fácil de entender, manter e testar.
O - Princípio do Aberto/Fechado (Open/Closed Principle): Entidades de software (classes, módulos, funções, etc.) devem estar abertas para extensão, mas fechadas para modificação. Isso significa que você deve ser capaz de estender o comportamento de uma classe sem modificar seu código-fonte original, o que é alcançado por meio de técnicas como herança, composição e interfaces.
L - Princípio da Substituição de Liskov (Liskov Substitution Principle): Os objetos de um programa devem ser substituíveis por instâncias de seus subtipos sem afetar a correção do programa. Em outras palavras, se S é um subtipo de T, então os objetos de tipo T podem ser substituídos por objetos de tipo S sem alterar as propriedades desejadas do programa.
I - Princípio da Segregação de Interfaces (Interface Segregation Principle): Uma classe não deve ser forçada a depender de métodos que ela não utiliza. Em vez de ter interfaces grandes e monolíticas, devemos preferir interfaces específicas para os clientes que as utilizam. Isso evita que as classes se tornem sobrecarregadas com métodos que não precisam implementar.
D - Princípio da Inversão de Dependência (Dependency Inversion Principle): Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações. Além disso, abstrações não devem depender de detalhes, mas detalhes devem depender de abstrações. Isso promove desacoplamento entre módulos e facilita a troca de implementações.
4- O que você sabe sobre testes de unidade
Resposta objetiva e suscinta:
Testes de unidade são uma prática de desenvolvimento de software onde pequenas unidades individuais de código são testadas de forma isolada para garantir que funcionem conforme o esperado. Essas unidades podem ser métodos, classes ou até mesmo partes específicas de um método.
O objetivo é identificar e corrigir problemas no código de forma antecipada, promovendo qualidade, confiabilidade e facilitando mudanças futuras. Os testes de unidade são automatizados e podem ser escritos usando frameworks como NUnit, MSTest ou xUnit.net em ambiente .NET.
Resposta mais detalhada:
Testes de unidade são uma prática fundamental no desenvolvimento de software, onde unidades individuais de código são testadas isoladamente para garantir que funcionem conforme o esperado. Uma unidade de código pode ser um método, uma classe, ou até mesmo uma pequena parte de um método, como uma função específica.
Os testes de unidade têm várias vantagens:
Identificação precoce de bugs: Ao testar unidades de código de forma isolada, os desenvolvedores podem identificar e corrigir bugs antes que eles se propaguem para outras partes do sistema.
Documentação viva: Testes de unidade servem como documentação viva do comportamento esperado de uma unidade de código. Isso ajuda os desenvolvedores a entenderem como a unidade deve ser usada e como ela se comporta em diferentes cenários.
Facilidade de manutenção: Unidades de código bem testadas são mais fáceis de manter e refatorar. Os testes fornecem um mecanismo de segurança que permite aos desenvolvedores fazer mudanças no código com confiança, sabendo que os testes ajudarão a identificar problemas se algo der errado.
Promoção da modularidade: Escrever testes de unidade incentiva a escrita de código modular e coeso, já que unidades isoladas são mais fáceis de testar.
No ecossistema .NET, os testes de unidade são frequentemente escritos usando frameworks como NUnit, MSTest, ou xUnit. Esses frameworks fornecem uma estrutura para escrever, organizar e executar testes de forma automatizada.
Além dos testes de unidade, também existem outros tipos de testes, como testes de integração, testes de aceitação e testes de sistema, que complementam os testes de unidade para garantir a qualidade do software em todos os níveis.
5- O que você sabe sobre a LINQ
Resposta objetiva e suscinta :
LINQ (Language Integrated Query) é uma extensão do C# que permite consultas integradas em coleções de dados, bancos de dados e XML. Ele oferece uma sintaxe de consulta expressiva e métodos de extensão para operações de consulta, simplificando o código e tornando-o mais legível.
Resposta mais detalhada :
E estamos conversados...
"Não vos inquieteis, pois, pelo dia de amanhã, porque o dia de amanhã cuidará de
si mesmo. Basta a cada dia o seu mal."
Mateus 6:34
Referências:
C# - Tasks x Threads. Qual a diferença
DateTime - Macoratti.net
Null o que é isso ? - Macoratti.net
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)