De uma forma mais ampla, podemos usar o conceito de generics quando uma classe/algoritmo pode ser aplicado a mais de um tipo de dados, ou seja, esta classe/algoritmo é independente do tipo de dado em questão; ao invés de criar várias versões da mesma classe (uma para cada tipo de dado desejado), criamos uma classe genérica e deixamos o compilador (no caso do .Net o compilador/Framework) trabalhar por nós.
O exemplo apresentado no post anterior é um caso clássico: em um programa podemos ter a necessidade de armazenar coleções de diversos tipos de dados diferentes; uma solução é implementar classes que armazenam tipos de dados específicos, contudo esta, geralmente, não é uma boa solução, visto que teremos duplicação de código, trabalho, etc. Uma segunda alternativa é a solução adotada até a versão 1.1 do .Net Framework, ou seja, escrever coleções de dados que manipulam o tipo de dado base (no .Net Object); pelas razões discutidas no post anterior (entre outras) esta também não é uma solução muito atraente; uma terceita alternativa seria mesclar as duas primeiras, ou seja, ter uma implementação da coleção que aceita a classe Object e escrever versões mais especializadas que utilizam esta implementação (melhorou bastante mas ainda temos que escrever e dar manutenção nas versões específicas). Generics nos permite escrever classes/algorítmos sem nos preocuparmos com o tipo de dado manipulado; para ser mais preciso manipulamos um tipo de dado que sera especificado pelo usuário do nosso código.
Assim, um tipo genérico (por exemplo a classe List<T> do namespace System.Collections.Generic) representa apenas um modelo, ou seja, não podemos criar instâncias deste tipo. Por exemplo o seguinte código não compila:
IList list = new System.Collections.Generic.List();
Se você tentar compilar este trecho de código em um programa C# o compilador emitirá um erro semelhante a:
TestGenericCollections.cs(9,3): error CS0305: Using the generic type 'System.Collections.Generic.List<T>' requires '1' type arguments
Este erro nos informa que o tipo List requer um type parameter, que nada mais é que um tipo de dados que será aplicado ao tipo genérico. Em outras palavras o compilador não tem como decidir se desejamos uma lista de bananas, nomes, idades, etc. Este dado (o tipo de lista que desejamos) deve ser informado ao compilador utilizando o type parameter T. Assim, para que o exemplo compile temos que dizer ao compilador qual é este tipo:
IList<string> list = new System.Collections.Generic.List<string>();
Um tipo generic deve possuir um ou mais type parameters que devem ser substuídos por tipos específicos no momento do uso do mesmo. Acho que aqui vale um exemplo:
0: using System;Neste exemplo definimos um tipo genérico (TestGeneric) que espera um Type Parameter (T) (Linha 2). Este tipo declara uma variável de instância data (Linha 31) do tipo T e também uma propriedade Data (Linha 9). Na linha 38 criamos uma instância da classe TestGeneric usando string como tipo para T, ou seja, tanto a variável de instância data quanto a propriedade Data assumem o tipo string para a variável t (linha 38).
1:
2: class TestGeneric<T>
3: {
4: public TestGeneric(T i)
5: {
6: data = i;
7: }
8:
9: public T Data
10: {
11: get
12: {
13: return data;
14: }
15:
16: set
17: {
18: data = value;
19: }
20: }
21:
22: public string Name
23: {
24: get
25: {
26: return name;
27: }
28: }
29:
30: private String name;
31: private T data;
32: }
33:
34: public class TestGenericDriver
35: {
36: private static void Main()
37: {
38: TestGeneric<string> t = new TestGeneric<string>("Adriano");
39: Type tt = t.GetType();
40:
41: Type[] defparams = tt.GetGenericArguments();
42: foreach (Type tp in defparams)
43: {
44: Console.WriteLine("\r\nType parameter: {0}", tp.Name);
45: }
46: }
47: }
Quando associamos um tipo a um type parameter dizemos que estamos criando uma instância do tipo genérico. Assim na linha 38 o trecho de código:
TestGeneric<string> t = new TestGeneric<string>("Adriano");instancia o tipo TestGeneric
Da mesma forma ao escrevermos o seguinte código:
TestGeneric<int> ti;estamos instanciando o tipo TestGeneric com T como int.
Apesar de neste exemplo em particular o tipo genérico possuir apenas um type parameter nada impede de definirmos tipos com dois ou mais type parameters.
Nos próximos posts pretendo discutir um pouco mais sobre:
- constrains.
- diferenças entre generics e templates C++.
- Palavra reservada default.
- Representação em runtime.
[+/-] mostrar/esconder este post
0 Comments:
Postar um comentário
<< Home