Generics em C#

Galera, estou me batendo aqui no C#.
Tenho duas dúvidas, que fazia no Java mas aqui não consigo fazer.

Ex:

public void metodo(MinhaClasse<?> clazzObj) {...}

Mas no C# não estou conseguindo fazer (esse ‘?’, não definindo explicitamente meu tipo), muito menos passar null em um parâmetro genérico, exemplo:

public class MinhaClasse<T> {
    public void meuMetodo(T t) {...}
}

porém ele não permite (no C#) passar um ‘null’ nesse parâmetro.

Alguém pra me dar um força?

Eh algo assim que vc ta querendo fazer ?
Tirei o exemplo da documentacao.

static void Swap&lt;T&gt;(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}
public static void TestSwap()
{
    int a = 1;
    int b = 2;

    Swap&lt;int&gt;(ref a, ref b);
    System.Console.WriteLine(a + " " + b);
}

sobre passar null nos parametros. Da uma olhada nisso:

//Daniel

No C# os wildcards não são necessários.

Você ainda poderia restringir o tipo de T.

Por exemplo, um método swap só para Ts filhos de UmaClasse:

static void Swap&lt;T&gt;(ref T lhs, ref T rhs) where T : UmaClasse { T temp; temp = lhs; lhs = rhs; rhs = temp; }

Ah sim, seu problema envolve listas.

Nesse caso, realmente não é possível fazer algo similar aos wildcards em C#:


http://www.jprl.com/Blog/archive/development/2007/Aug-31.html

Embora a implementação de generics dele traga outras vantagens (como vc poder chamar new T, testar se o objeto é instanceof T ou saber o tipo de T por reflection), essa vantagem ele não tem.

[quote=ViniGodoy]Ah sim, seu problema envolve listas.

Nesse caso, realmente não é possível fazer algo similar aos wildcards em C#:


http://www.jprl.com/Blog/archive/development/2007/Aug-31.html

Embora a implementação de generics dele traga outras vantagens (como vc poder chamar new T, testar se o objeto é instanceof T ou saber o tipo de T por reflection), essa vantagem ele não tem.[/quote]

Ah ok, obrigado pelas respostas.
Mas então vc teria uma ‘solução alternativa’ para este problema?

E para os generics serem nullable, tem como?

[quote=windsofhell]Eh algo assim que vc ta querendo fazer ?
Tirei o exemplo da documentacao.

static void Swap&lt;T&gt;(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}
public static void TestSwap()
{
    int a = 1;
    int b = 2;

    Swap&lt;int&gt;(ref a, ref b);
    System.Console.WriteLine(a + " " + b);
}

sobre passar null nos parametros. Da uma olhada nisso:

//Daniel[/quote]

Vlw pela resposta, mas não é bem isso não cara…
E sobre o link, já li esse, mas não consegui usá-lo aqui, acho que não resolve o problema…

Os generics só são nullables se eles forem exclusivamente para classes. É que no C# tipos primitivos podem assumir generics.

Se você quiser manter a possibilidade de tipos primitivos, ao invés de null, você usa default:

Isso vai ser null para tipos de referência, 0 para tipos numéricos, false para booleans.

Se você realmente quiser restringir a tipos de referência, coloque uma claúsula where:

public class MinhaClasse&lt;T&gt; where : class { }

O class ali no final indica que o tipo é de referência: pode ser classe ou interface. Como agora o C# sabe que se trata de um tipo assim, variáveis de um tipo T poderão receber null.

Para listas, eu tenho trabalhado com extension methods. Mas nunca precisei do wildcard “super”.
Para coisas mais simples, dá para usar a sintaxe do windsofshell. Ela criará um método diferente para cada tipo de lista:

public void metodo&lt;V&gt;(MinhaClasse&lt;V&gt; clazzObj) where V : class { }

Talvez até funcione isso aqui no lugar do super, mas nunca testei:

public void metodo&lt;T, Z&gt;(MinhaClasse&lt;V&gt; clazzObj, Comparator&lt;Z&gt; comparador) where V : class, Z : V
{
}

[quote=ViniGodoy]Os generics só são nullables se eles forem exclusivamente para classes. É que no C# tipos primitivos podem assumir generics.

Se você quiser manter a possibilidade de tipos primitivos, ao invés de null, você usa default:

Isso vai ser null para tipos de referência, 0 para tipos numéricos, false para booleans.

Se você realmente quiser restringir a tipos de referência, coloque uma claúsula where:

public class MinhaClasse&lt;T&gt; where : class { }

O class ali no final indica que o tipo é de referência: pode ser classe ou interface. Como agora o C# sabe que se trata de um tipo assim, variáveis de um tipo T poderão receber null.[/quote]

Vlw Vini, esse ‘where T: class’ resolveu parte dos meus problemas, mas surgiram outros. Vou colocar o código aqui para poderem me ajudar…

public interface Formatter&lt;T&gt; where T: class
    {
        String format(T t);
        T parse(String s);
    }

public class DateFormatter: Formatter&lt;DateTime&gt;
{...}

mas ele acusa o seguinte erro:

Error 2 The type ‘System.DateTime’ must be a reference type in order to use it as parameter ‘T’ in the generic type or method ‘Model.Formatter.Formatter<T>’

DateTime para o C# é um tipo primitivo.

Se você precisa usar com DateTime, use o default(T) e tire o where T : class

Aliás, faz até mais sentido, já que nada te impede de criar um formatador para floats e ints.

Outra opção é usar um wrapper, como DateTime?

(A ? é parte no nome do tipo).

[quote=ViniGodoy]DateTime para o C# é um tipo primitivo.

Se você precisa usar com DateTime, use o default(T) e tire o where T : class

Aliás, faz até mais sentido, já que nada te impede de criar um formatador para floats e ints.

Outra opção é usar um wrapper, como DateTime?

(A ? é parte no nome do tipo).[/quote]

Não consegui entender esse default, não seria só para atribuição de valores?

E usando o wrapper DateTime?, ele aponta o erro…

Error 2 The type ‘System.DateTime?’ must be a reference type in order to use it as parameter ‘T’ in the generic type or method ‘Model.Formatter.Formatter<T>’

Onde você quer passar o null?

Em um método que recebe o ‘T’…

View o erro que postei?
Parece que o DateTime? não e unm wrapper…

Pois é, aparentemente no C# os wrappers não são considerados reference types. Curioso, não sabia dessa…

Em todo caso, você só precisa do where T : class se for restringir seu formatador a tipos de referência. Como você vai usar com DateTime, não parece ser o caso.
Simplesmente retire esse where, instancia o fomatador para DateTime? e você poderá passar null no método.

[quote=ViniGodoy]Pois é, aparentemente no C# os wrappers não são considerados reference types. Curioso, não sabia dessa…

Em todo caso, você só precisa do where T : class se for restringir seu formatador a tipos de referência. Como você vai usar com DateTime, não parece ser o caso.
Simplesmente retire esse where, instancia o fomatador para DateTime? e você poderá passar null no método.[/quote]

Esse é o problema, tirando o 'where T: class", quando faço o seguinte:

protected override List&lt;Formatter&gt;&lt;T&gt;&gt; DefaultFormatters&lt;T&gt;()
        {
            List&lt;Formatter&gt;&lt;T&gt;&gt; formatters = new List&lt;Formatter&gt;&lt;T&gt;&gt;();
            formatters.Add(new DateFormatter(DateFormatter.DATE));
            formatters.Add(new DateFormatter(DateFormatter.TIME));
          
            return formatters;
        }

o seguinte erro é apresentado:

Error 9 The best overloaded method match for ‘System.Collections.Generic.List<Model.Formatter.Formatter><T>>.Add(Model.Formatter.Formatter<T>)’ has some invalid arguments

Desculpe a insistência, mas senti algumas facilidades do Java em relação ao C#…

Ué, mas seu método DefaultFormatters retorna formatos para um tipo T?
Isso não seria possível nem no Java, já que com wildcards o método add seria bloqueado.

Se o tipo T fosse int, por exemplo, como você poderia retornar dois formatters de Date?

[quote=ViniGodoy]Ué, mas seu método DefaultFormatters retorna formatos para um tipo T?
Isso não seria possível nem no Java, já que com wildcards o método add seria bloqueado.

Se o tipo T fosse int, por exemplo, como você poderia retornar dois formatters de Date?
[/quote]

Desculpe o erro, estava retornando formatters para Object, mas assim não vai funcionar, por isso queria uma solução alternativa, pois no Java posso usar o ‘?’…

PS: Não estou na empresa agora, vou fazer mais testes amanha…