Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Quando um tipo ou método genérico é compilado em CIL (linguagem intermediária comum), ele contém metadados que o identificam como tendo parâmetros de tipo. Como o CIL para um tipo genérico é usado difere com base em se o parâmetro de tipo fornecido é um tipo de valor ou tipo de referência.
Quando um tipo genérico é construído pela primeira vez com um tipo de valor como parâmetro, o runtime cria um tipo genérico especializado com o parâmetro ou parâmetros fornecidos substituídos nos locais apropriados no CIL. Tipos genéricos especializados são criados uma vez para cada tipo de valor exclusivo que é usado como um parâmetro.
Por exemplo, caso o código do programa declare uma pilha construída de inteiros:
Stack<int>? stack;
Neste ponto, o runtime gerará uma versão especializada da classe Stack<T> com o inteiro substituído corretamente, de acordo com seu parâmetro. Agora, sempre que o código do programa utilizar uma pilha de inteiros, o runtime reutilizará a classe especializada Stack<T> gerada. No exemplo a seguir, duas instâncias de uma pilha de inteiros são criadas e compartilham uma única instância do Stack<int> código:
Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();
No entanto, suponha que outra Stack<T> classe com um tipo de valor diferente, como um long ou uma estrutura definida pelo usuário como parâmetro, seja criada em outro ponto do código. Como resultado, o runtime gerará outra versão do tipo genérico e substituirá um long nos locais apropriados no CIL. As conversões não são mais necessárias porque cada classe genérica especializada contém nativamente o tipo de valor.
Os genéricos funcionam de forma um pouco diferente para tipos de referência. Na primeira vez que um tipo genérico é construído com qualquer tipo de referência, o runtime cria um tipo genérico especializado com referências de objeto substituídas pelos parâmetros no CIL. Em seguida, sempre que um tipo construído é instanciado com um tipo de referência como seu parâmetro, independentemente do tipo que ele é, o runtime reutiliza a versão especializada criada anteriormente do tipo genérico. Isso é possível porque todas as referências têm o mesmo tamanho.
Por exemplo, suponha que você tenha dois tipos de referência, uma Customer classe e uma Order classe, e também suponha que você criou uma pilha de Customer tipos:
class Customer { }
class Order { }
Stack<Customer> customers;
Neste ponto, o runtime gera uma versão especializada da Stack<T> classe que armazena referências de objeto que serão preenchidas posteriormente em vez de armazenar dados. Suponha que a próxima linha de código crie uma pilha de outro tipo de referência, com o nome Order:
Stack<Order> orders = new Stack<Order>();
Ao contrário dos tipos de valor, outra versão especializada da Stack<T> classe não é criada para o Order tipo. Em vez disso, uma instância da versão especializada da Stack<T> classe é criada e a orders variável é definida para referenciá-la. Imagine que uma linha de código foi encontrada para criar uma pilha de um tipo Customer:
customers = new Stack<Customer>();
Assim como acontece com o uso anterior da Stack<T> classe criada usando o Order tipo, outra instância da classe especializada Stack<T> é criada. Os ponteiros contidos nela são definidos para referenciar uma área de memória do tamanho de um Customer tipo. Como o número de tipos de referência pode variar descontroladamente de programa para programa, a implementação em C# de genéricos reduz consideravelmente a quantidade de código reduzindo para um o número de classes especializadas criadas pelo compilador para classes genéricas de tipos de referência.
Além disso, quando uma classe C# genérica é instanciada usando um tipo de valor ou parâmetro de tipo de referência, a reflexão pode consultá-la em tempo de execução e seu tipo real e seu parâmetro de tipo podem ser apurados.