Conceitos: Como chamar um Delegate

Como chamar um Delegate? Questões sobre delegates são incomuns em provas para candidatos iniciantes, aparecem com alguma frequência em vagas para profissionais plenos e é “figurinha carimbada” na maioria das provas para seniores.

Entretanto, nem sempre é uma pergunta difícil. Para acertar a questão abaixo, o candidato precisa apenas conhecimentos básicos sobre o assunto.

Qual(ais) da(s) próximas alternativas substitui(em) ***** na função main fazendo com que o programa retorne o valor 15?

using System;

namespace Program {
   class Program {
      static void Main(string[] args) {
         int result;

         *****

         Console.WriteLine(result);
      }

      static int Add(int a, int b) {
         return a + b;
      }

      static int DoIntMath(Func<int,int,int> function, int x, int y) {
         return function(x, y);
      }    
   }
}

A. result = DoIntMath(new delegate int(int a, int b){ return a + b; }, 5, 10);
B. result = DoIntMath(Add, 5, 10);
C. result = DoIntMath((a, b) => (a + b),  5,  10);
D. result = DoIntMath(delegate (int a, int b){ return a + b; }, 5, 10);
E. result = DoIntMath(Add(5, 10));

Mas o que é um delegate?

Um delegate é um tipo que permite fazer referência para um método com uma lista de parâmetros e um tipo de retorno específico. Ou seja, é um ponteiro que pode ser associado a qualquer método com uma assinatura e tipo de retorno compatíveis.

A função DoIntMath

static int DoIntMath(Func <int, int, int> function, int x, int y)

… possui três parâmetros. Os dois últimos (x e y ) são parâmetros inteiros comuns, já o primeiro (function) é uma função que retorna um número inteiro e recebe dois parâmetros também inteiros: Func< retorno, parâmetro 1, parâmetro 2>.

A função function aceita qualquer coisa com o formato int(parâmetro int, parâmetro int). Em outras palavras, para invocar a função DoIntMath, é preciso passar uma função semelhante à int Add(int a, int b), o que já elimina a alternativa “E”. A alternativa “A” também pode ser facilmente eliminada por causa da palavra new, uma vez que não se instancia um delegate.

Com isto, restam como corretas as alternativas “B”, onde é passada uma função já existente (Add) e as alternativas “C” e “D”, onde são passadas novas funções que foram declaradas diretamente no parâmetro function.

Origem da questão
País: Holanda Tipo: Conceitos Assunto: Delegate
Ramo de negócio da empresa: Consultoria Grau de Dificuldade: Médio

Roteiro de Entrevista: Desenvolvedor para empresa de apostas da Irlanda

Costumo fazer uma entrevista técnica a cada quinze dias. Vou passar a publicar o roteiro das entrevistas aqui, começando por uma fiz na semana passada para o cargo de Desenvolvedor Sênior de uma empresa de apostas da Irlanda. Na empresa não ganha dinheiro diretamente com as apostas, mas sim com um algoritmo que gera as estatísticas (rates) para os possíveis resultados de um jogo, inclusive quando ele está em andamento. Uma das principais ocupações lá é a manipulação de grandes arquivos textos, com dados desconhecidos e que necessitam de um rigoroso controle de memória.

Algumas das perguntas que me foram feitas durante a entrevista, que durou quase uma hora e meia, estão listadas abaixo.

Arquitetura e padrões

  • O que é RestAPI? O que faz uma API ser REST?
  • Quais são os princípios SOLID? Você concorda com todos eles? Se você tivesse que escolher apenas um deles para seguir, qual seria?
  • Quais os design patterns da Gang of Four você conhece? Explique o pattern decorator e dê um exemplo prático de quando ele pode ser usado.
  • Dê um exemplo de quando você utilizaria um banco de dados NoSQL ao invés de um banco transacional

C#

  • O que é Injeção de Dependência (dependency injection)? Quais são as principais vantagem do seu uso? Você vê desvantagens neste tipo de abordagem? Quais os frameworks de Injeção de Dependência que você conhece/já trabalhou?
  • O que é o Garbage Collection e como ele funciona?
  • Do que trata a classe IDisposable? Cite um exemplo de quando se deve implementá-la.
  • O Linq (Language-Integrated Query) pode ser usado em quais fontes de dados (data sources)?
  • O que são tipos anônimos (anonymous type)?
  • Qual a diferença entre value type e reference type? Um objeto do tipo value type é armazenado na mémoria heap ou na stack?
  • O que são tipos dinâmicos (dynamic type)?
  • Por que um string é imutável? Se ela não fosse, quais seriam as consequências?
  • Diferencie threads de tasks.

Comentários sobre a questão envolvendo Princípios SOLID

Como pode ser visto não há nada de muito surpreendente nas perguntas, com exeção da questão sobre os princípios SOLID. Não a questão para explicar os princípios, esta é recorrente. Tão recorrente que, eu diria, está presente na maioria absoluta das entrevistas. O que surpreendeu foram as duas questões que se seguiram a partir dela.

Você concorda com todos eles? Respodi que sim, mas com restrições. Na verdade acho que o princípio open-close que diz que “as entidades devem ser abertas (open) para extensão mas fechadas (close) para alterações” pode ser quebrado quando a nova funcionalidade estiver diretamente relacionada com o que faz a classe em questão.

Por exempo, imagine que você possui uma classe que valida um conjunto de dados contendo datas e números. Depois de algum tempo, uma nova regra surge e é preciso também passar a validar se um determinado campo string possui apenas os valores válidos (por exemplo, a lista de estados brasileiros). Esta validação deveria estar em uma nova classe? Não, então o princípio open-close foi quebrado.

Se você tivesse que escolher apenas um deles para seguir, qual seria? Eu disse que a resposta óbvia aqui seria o primeiro deles (responsabilidade única – single responsability), mas que eu optaria pelo princípio de substituição de Liskov (Liskov Substitution), que diz que “se S é um subtipo de T, então os objetos do tipo T”devem poder ser substituídos pelos objetos de tipo S sem que seja necessário alterar as propriedades do programa”.

O motivo da escolha dele é que para não violá-lo as abastrações precisam ser muito bem estruturadas, ou seja, a arquitetura do software precisa ser muito bem pensada. Além disto, provavelmente será preciso usar injeção de dependência, que está diretamente correlacionado com outro princípio do SOLID: Inversão de Dependência – Dependency Inversion, o que significa que minhas classes terão baixo acoplamento, sendo assim fáceis de serem mantidas.

Roteiro de Entrevista
País: Irlanda Cargo: Desenvolvedor Sênior
Ramo de negócio da empresa: Apostas

Conceitos: Tipos Anônimos (anonymous type)

Qual das próximas afirmações descrevem corretamente o código C# abaixo (selecione todas as corretas):

var pessoa = new {Id = 1, Nome = "Maria"};

A. O código tem um erro e não compila

B. pessoa pode ser reatribuída da seguinte forma: pessoa = new {Idade = 25, Nome = “Pedro”};

C. pessoa é do tipo anônimo (anonymous type)

D. pessoa deriva de Object.

E. Os atributos Id e Nome são somente leitura (read-only)

A primeira coisa a ser feita para resolver esta questão é analisar o código, que apresenta a criação de um novo objeto chamado pessoa, sem no entanto especificar um tipo. Portanto, trata-se de um “tipo anônimo” (anonymous type, em inglês).

Os Tipos Anônimos fornecem uma maneira rápida de encapsular um grupo de propriedades em único objeto, sem que seja necessário definir explicitamente o seu tipo. Os tipos dos atributos são inferidos pelo compilador e não estão disponíveis ao nível do código fonte. Além disto, eles são somente leitura (read-only).

Agora que você já sabe o que é um tipo anônimo, vamos analisar as opções:

  • Letra A:  errada, pois o código compila perfeitamente;
  • Letra B: errada, não é possível reatribuir um tipo anônimo;
  • Letra C: correta, trata-se de um tipo anônimo;
  • Letra D: correta, tupo em C# deriva de Object; e
  • Letra E: correta, já que os atributos de tipos anônimos são somente leitura;

Sendo assim, estão corretas as letras C, D e E.

Atenção

Não confunda um tipo anônimo com a nova forma de instânciar objetos introduzida no C#9, chamada target-typed object creation. Ela permite instanciar um objeto sem usar o nome da classe depois da palavra reservada new, desde que a classe tenha sido explicitamente declarada antes do nome do objeto (mais informações em docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new – texto em inglês).

public class Pessoa {
   public int Id;
   public string Nome;
}

(...)

//a classe "Pessoa" eh explicitamente declarada antes do objeto "pessoa", 
//não sendo necessario repetir a declaracao depois da palavra  "new"
Pessoa pessoa = new(1, "Maria");
Origem da questão
País: Holanda Tipo: Conceitos Assunto: Tipos Anônimos
Ramo de negócio da empresa: Consultoria Grau de Dificuldade: Médio

Teste de código: contando os vales percorridos

Enunciado em inglês. Desde já, tenha em mente que o texto foi parcialmente alterado para diminuir a chance da solução seja encontrada por alguém que esteja fazendo a prova:

A hiker keeps records of their hikes registering when is uphill, sea level, or downhill. Hikes always start and end at sea level, and each step up or down represents a  unit change in altitude. We define the following terms:

    • A hill is a sequence of consecutive steps above sea level, starting with a step up from sea level and ending with a step down to sea level.
    • valley is a sequence of consecutive steps below sea level, starting with a step down from sea level and ending with a step up to sea level.

Given the sequence of up and down steps during a hike, find and print the number of valleys walked through.

Sample Input: 8, UDDDUDUU

Expected result: 1

Explanation: If we represent _ as sea level, a step up as /, and a step down as \, the hike can be drawn as:

_/\      _
   \    /
    \/\/

Explicando

Um caminhante registra todos seus passos durante suas caminhadas (a redundância está no texto original). Ele sempre começa as caminhadas no nível do mar, sempre que sobe registra um U (up) e sempre que desce registra um D (down).

No exemplo dado, o caminhante começa subindo um nível uma colina (UDDDUDUU), imediatamente após desce um nível (UDDDUDUU), o que faz com que ele volte ao nível do mar e depois começa a descer um vale (UDDDUDUU), sobre um nível (UDDDUDUU), volta a descer (UDDDUDUU) e depois sobe novamente até o nível do mar (UDDDUDUU). O exercício pede que seja retornado o número de vales pelo qual o caminhante passou (neste caso: 1).

Resolvendo

Há várias formas de resolver o problema, uma delas é ir contado o número de passos para cima e para baixo. Quando eles são iguais e o próximo passo for para baixo, está começando a descer um vale. Uma vez que o  número de passos volte a ser iguais, parou de descer e voltou ao nível do mar. Apesar de funcionar, não é uma solução das mais eficiente.

Outra forma, com melhor performance, é comparar o nível em que o caminhante se encontra. Considere zero como sendo o nível do mar, cada vez que subir adicione uma unidade ao nível, quando ele descer, subtraia uma unidade. Desta forma, quando o nível for menor que zero, o caminhante se encontra num vale., quando o nível voltar a zero, ele saiu do vale. Então, basta contar quantas vezes ele entrou no vale.  Abaixo o código devidamente comentado:

int contandoVales(int passos, string caminho) {
    int nivel = 0; //nivel do mar
    int numeroVales = 0;
    bool estaNumVale = false; //comeca no nivel do mar
    char desce = 'D';
 
    //percorre toda a string "caminho"
    for (int i = 0; i < passos; i++)
    {
        //se a posicao for igual a "D" subtrai uma unidade do "nivel"
        //senao adiciona uma unidade (este é um IF ternário)
        nivel = caminho[i] == desce ? --nivel : ++nivel;

        //se o nível for menor que zero e nao está no vale        
        if (nivel < 0 && !estaNumVale)
        {
            numeroVales++; //incrementa o número de vales
        }

        //se nível for menor que zero está no vale
        estaNumVale = nivel < 0;
    }

    return numeroVales;
}
Origem da questão
País: Bélgica Tipo: Teste de Código Assunto: Manipulação de strings
Ramo de negócio da empresa: Banco Grau de Dificuldade: Fácil

Teste de código: encontrar o maior número possível

O texto e valores do enunciado em inglês foram ligeiramente alterados para diminuir a chance da solução seja encontrada por alguém que esteja fazendo a prova:

Write a function that, given an integer N, returns the maximum possible value obtainable by deleting one ‘3’ digit from the decimal representation of N.

Examples: Given N = 13938, the function should return 1938.  Given N = -3839, the function should return -389. Given N = -30, the function should return 0. After deleting the ‘3’, the only digits in the number are zeroes, so its value is 0.

Assume that:

    • N is an integer within the range -99999999 to 99999999;
    • N contains at least one ‘3’ digit in its decimal representation;
    • N consists of at least two digits in its decimal representation.

In your solution, focus on correctness. The performance of your solution will not be the focus of the assessment.

Enunciado (resumido) em português

Dado um número inteiro N, retorne o maior valor possível ao remover o dígito 3 do número. Por exemplo: dado N= 393, deve retornar 93. Se N = 3153, retorna 315. Se N= -323, retorna -23. Por fim, se N = -30, retorna 0 (zero).

Assuma que sempre haverá um 3 no valor informado e este valor sempre terá pelo menos duas casas decimais.

Apesar de receber um número inteiro, trata-se de uma questão de parse de strings, uma vez que o valor precisa ser convertido para string para ser alterado (é possível trabalhar apenas com números, mas isto torna a questão muito mais complexa).

Para resolver o problema vamos pegar o valor 3153. Se remover o primeiro “3”, retorna 153, se remover o segundo 315, que é a resposta esperada.

Uma das formas de resolver o problema é identificar as posições da string que possuem o 3 e depois eliminar estas posições (indexes) uma a uma, comparando o resultado, o que gera o seguinte algoritmo em português estruturado

1. Converta o número para string
2. Percora a string identificando as posições onde o valor = 3
3. Armaze as posições num vetor 
4. Inicialize uma variável maiorValor com o menor valor possível
5. Para cada uma das posições do vetor faça
   6. Remova o caracter da posição na string original
   7. Compara o valor de maiorValor com o novo valor gerado
   8. Se o novo valor for maior, armaze no valor em maiorValor
9. Fim do para
10. Retorne o maior valor

A solução em C#

using System;
using System.Collections.Generic;

class Solution {
    //retorna um vetor com as posicoes da string "number" 
    //ocupadas pelo numero 3
    //ex: 635131 retorna [1,4] => a contagem comeca em 0
    private static List<int> GetIndexes(string number) {
        //cria uma lista (que sera retornada)
        var indexes = new List<int>();

        //percore todos os caracteres da string
        //number.Length = 6, para o numero 635131 
        for(int i = 0; i< number.Length; i++) {
            //se caracter = 3, adiciona o indice na lista
            if (number[i] == '3') {
                indexes.Add(i);
            }
        }
        return indexes;
    }

    public int solution(int N) {
        //conver o numero N para uma string
        var originalNumber = N.ToString();
        //pega a lista de indices
        var indexes = GetIndexes(originalNumber);
        //seta o maior valor como o menor inteiro existe
        //assim qualquer valor gerado serah maior q ele
        var maxValue = int.MinValue;

        //percore todos os elementos do vetor
        foreach(var index in indexes) {
           //remove o caracter da posicao armazenada em GetIndexes()
            var number = originalNumber.Remove(index,1);
            //transforma a string num numero inteiro
            var newNumber = int.Parse(number);

            //se o "valor maximo" for menor que o "novo valor"
            //seta o "valor maximo" como sendo o "novo valor"
            if (maxValue < newNumber) {
                maxValue = newNumber;
            }
        }
        return maxValue;
    }
}

 

Origem da questão
País: Irlanda Tipo: Teste de Código Assunto: manipulação de strings
Ramo de negócio da empresa: Pagamentos Grau de Dificuldade: Fácil

Banco de Dados: Select recursivo e subselect em tabelas com autorelacionamento

Questão de Entrevista
País: Irlanda Tipo: Técnica Assunto: Banco de Dados
Ramo de negócio da empresa: Financeiro (Banco) Grau de Dificuldade: Média

 

Data a tabela Employee, escreva consultas que retorne:

  1. O nome e o salário dos empregados que recebem mais do que os seus gerentes (manager) diretos, ordenando o resultado pelo salário de forma decrescente
  2. O nome dos empregados que possuem exatamente outros dois empregados com o mesmo dia e mês de aniversário.

tabela employee (empregado)

Abaixo o script para criar e inserir os dados da tabela (padrão ANSI, pode ser usado em qualquer banco de dados)

-- cria a tabela Employee (Empregado)
-- com chave primária nomeada como PK_Employee 
CREATE TABLE dbo.Employee(
  Id int NOT NULL,
  Name varchar(50) NOT NULL,
  Birthday datetime NOT NULL,
  Salary numeric(18, 2) NOT NULL,
  ManagerId int,
  CONSTRAINT PK_Employee 
  PRIMARY KEY (Id)
)

-- cria uma chave estrangeira (foreign key) 
-- relacionando o gerente (ManagerId) ao empregado (Id)
ALTER TABLE dbo.Employee  
WITH CHECK ADD CONSTRAINT FK_Employee_Employee 
FOREIGN KEY(ManagerId)
REFERENCES dbo.Employee (Id)

--insere registros na table a Employee 
INSERT INTO Employee 
       (Id,Name,Birthday,Salary,ManagerId) 
VALUES (1, 'John', '1978-02-22', 5000, null)

-- como todos os campos estão sendo usados, podemos simplificar 
-- o comando ocultado p nome das colunas na instrução INSERT
INSERT INTO Employee VALUES (2, 'Mary', '1983-08-25', 5200, 1)
INSERT INTO Employee VALUES (3, 'Paul', '1994-11-07', 3750, 2)
INSERT INTO Employee VALUES (4, 'Anna', '2001-12-18', 2450, 3)
INSERT INTO Employee VALUES (5, 'Aoifa', '1994-11-07', 4200, 3)
INSERT INTO Employee VALUES (6, 'Emily', '1995-03-05', 3200, 1)
INSERT INTO Employee VALUES (7, 'Alan', '1997-05-26', 3800, 3)
INSERT INTO Employee VALUES (8, 'Lucy', '2001-11-26', 3800, 2)
INSERT INTO Employee VALUES (9, 'Adan', '1989-08-25', 4760, null)
INSERT INTO Employee VALUES (10, 'Will', '2001-12-01', 2800, null)
INSERT INTO Employee VALUES (11, 'Sophia', '2002-04-08', 2930, 10)
INSERT INTO Employee VALUES (12, 'Emma', '1998-09-20', 2560, 10)
INSERT INTO Employee VALUES (13, 'James', '1995-08-25', 2560, 9)
INSERT INTO Employee VALUES (14, 'Charlotte', '1995-10-12', 2980, 9)
INSERT INTO Employee VALUES (15, 'Lucas', '1986-01-11', 2900, 10)

Vamos a questão 1: retornar o nome e o salário dos empregados que recebem mais do que os seus gerentes diretos, ordenando os registros pelo salário de forma decrescente.

Analisando os dados nota-se que a coluna ManagerId é uma auto-referência para a tabela Employee, ou seja, indica o registro na tabela que se refere ao gerente da pessoa em questão. Por exemplo: John (Id = 1) não tem um gerente direto, pois seu ManagerId é nulo. Já Mary (Id= 2) é subordinada a John, pois seu ManagerId é igual 1, que é o Id de John. Mary tem um salário de $5200, enquanto John tem um salário de $5000, logo Mary deve ser listada.

Uma das formas de resolver este problema é usando um subselect e comparar o salário do cada registro da tabela Employee com o salário do seu respectivo gerente. Como quem não tem gerente direto não precisa ser listado, por questões de desempenho pode-se adicionar um filtro para ignorar estas pessoas, com isto o subselect não será executado nestes casos (o engine do banco de dados sabe que uma expressão com subselect é mais cara, computacionalmente falando, que uma expressão que usa apenas um campo da própia tabela, como o operador condicional AND (E) exige que as duas expressões sejam verdadeira para ser verdadeiro, ele executa primeiro a condição e.ManagerId is not null e ignora a segunda expressão se a primeira for falsa):

select Name, Salary 
from Employee e
where e.ManagerId is not null
  and e.Salary > (select Salary 
                  from Employee e2 
                  where e2.Id = e.ManagerId)
order by e.Salary desc

Executando o comando acima:

Name Salary
Mary 5200.00
Aoifa 4200.00
Alan 3800.00
Sophia 2930.00
Lucas 2900.00

Outra forma de resolver o problema é fazer um INNER JOIN da tabela Employee com ela mesma. Esta forma, além de ter melhor performance, permite listar o nome e o salaário do gerente, o que facilita a verificação do resultado (isto está fora do escopo do exercício inicial, se o exercício for corrigido eletronicamente, devolva exatamente o que se pede. Se for uma programação em par – entrevistador e entrevistado – você pode usar mas deve destacar o que está fazendo):

select e.Name, e.Salary, e2.Name Manager, e2.Salary 
from Employee e
inner join (select Id, Name, Salary from Employee) e2 
      on e2.Id = e.ManagerId
where e.Salary > e2.Salary
order by e.Salary desc

Perceba que no exemplo acima a condição e.ManagerId is not null foi suprimida. Como foi usado um INNER JOIN, que obriga que o registro exista dos dois lados da junção, os registros com ManagerId = NULL são automaticamente descartados.

Name Salary Manager Salary
Mary 5200.00 John 5000.00
Aoifa 4200.00 Paul 3750.00
Alan 3800.00 Paul 3750.00
Sophia 2930.00 Will 2800.00
Lucas 2900.00 Will 2800.00

Agora a questão 2: listar o nome nome dos empregados que possuem exatamente outros dois empregados com o mesmo dia e mês de aniversário.

Para responder a questão vamos usar a mesma base de comando da primeira opção de resposta da questão 1, com alterações na cláusula where da subconsulta. Para comprar os dias e meses foram usadas, respectivamente, as funções DAY e MONTH (estas funções são do SQL Server, consulte a documentação do seu banco de dados para encontrar a função equivalente).

select Name
from Employee e
where (select count(*) 
       from Employee e2 
       where e2.Id <> e.Id
         and day(e2.Birthday) = day(e.Birthday) 
         and month(e2.Birthday) = month(e.Birthday)) = 2

O resultado é:

Name
Mary
Adan
James

Note que Aoifa e Paul também fazem aniversário no mesmo dia e mês (07 de novembro), mas o exercício pede que sejam listadas apenas o empregados que possuem exatamente outros dois com o mesmo dia e mês de aniversário, por isto a inclusão da condição e2.Id <> e.Id na cláusula where da subconsulta.

O que é Injeção de Dependência? Vantagens e desvantagens

Uma das perguntas que mais ouço em entrevistas é: o que é Injeção de Dependência? Normalmente esta pergunta vem acompanhada de outras duas: (1) quais suas vantagens e desvantagens? e (2) que frameworks você conhece que implementam este pattern (padrão)?

O que é Injeção de Dependência?

Injeção de Dependência (DI, do termo em inglês dependency injection) é um padrão de desenvolvimento que implementa Inversão de Controle (IoC – inversion of control – um dos cinco princípios SOLID) para resolver dependências entre classes, mantendo as classes com baixo nível de acomplamento. Neste tipo de solução, ao invés de instanciar objetos diretamente ou usar referências estáticas, os objetos são fornecidos (“injetados”) por um framework sempre que necessário, normalmente através do construtor ou de propriedades.

Acoplamento, neste contexto, é a conexão/dependência/interação entre classes. Quanto maior for o acoplamento, maior a coesão e mais difícil e trabalhosa é a manutenção do sistema.

Pode-se implementar de três maneiras diferentes, através de:

  1. Constructor: modo em que implementa-se DI a na definição dos construtores das classes;
  2. Getter e Setter: implementa DI através dos métodos get e set das propriedades;
  3. Service Locator: usa-se classes que servem como “localizadoras” de objetos que são instanciados na classe.

Injeção de Depêndencia: vantagens e desvantagens

As principais vantagens são:

  • Se não elimina, ou ao menos reduz significativamente a dependência entre as classes;
  • Aumenta a reusabilidade de componentes – como eles não sabem como as classes das quais dependem são implementadas, tornam-se mais reutilizáveis;
  • Reduz a complexidade do módulo (classe);
  • Facilita a execução de testes unitários (unit tests), uma vez que este tipo de padrão utiliza interfaces, que podem ser facilmente “mockadas”; e
  • Tona o código mais legíveis e, por consequência, facilita a manutenção projeto;

Dentre as desvantagens, pode-se citar:

  • Aumenta a complexidade do projeto e pode requer mais esforço de desenvolvimento inicial;
  • Faz com que os clientes exigem detalhes de configuração fornecidos pelo código de construção;
  • Torna o debug mais difícil, pois torna o código mais difícil de rastrear uma vez que separa o comportamento da construção da classe que é injetada.

Frameworks para Injeção de Depêndencia

Além do .NET, há outros frameworks que implementam injeção de depêndencia, os mais conhecidos são:

  • Unity (unitycontainer.org): projeto criado e desenvolvido pela Microsoft e entregue à comunidade em 2015. Suporta .NET Framework 4.0+, .NET Core e .NET Standard 1.0 e 2.0, o código pode ser baixado em github.com/unitycontainer/unity. Número de download no NuGet: 44 milhões.
  • Autofac (autofac.org): projeto open source com código disponível em github.com/autofac/Autofac. Suporta .NET Core, ASP.NET Core, .NET Standard 2.1 e .NET Framework 4.5.1+. Número de download no NuGet: 38,8 milhões.
  • Ninject (ninject.org): sua versão mais atual (3.3.4) suporta .NET Framework 4.5 e .NET Standard 2.0. O código está disponível em github.com/ninject/ninject. Número de download no NuGet: 23,4 milhões.
  • Castle Windsor (castleproject.org/projects/windsor): mais um projeto open source hospedado no GitHub github.com/castleproject/Windsor. Sua versão mais atual (5.1.1) suporta .NET Framework 4.5 e .NET Standard 1.6. Número de download no NuGet: 20,4 milhões.