Pergunta

Eu tenho uma classe base:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

E duas classes derivadas

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

Ok, agora dizem que eu tenho uma lista de itens:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

e eu quero que eles para chamar seu método DoSomething (). Eu poderia esperar apenas iterate a lista e chamar seu método DoSomething (), então vamos dizer que eu tenho um método para fazer isso chamado AllDoSomething () que interage apenas sobre a lista e faz o trabalho:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

Qual é a diferença prática do método seguinte?

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

Ambos os métodos parecem, em termos reais, apesar de ser sintaticamente diferente, estar fazendo a mesma coisa.

Eles são apenas maneiras diferentes de fazer a mesma coisa? Eu entendo genéricos e tipo restrições, mas não pode ver porque eu usaria uma forma sobre o outro neste caso.

Foi útil?

Solução

Isso ocorre porque como ainda, C # não suporta Covariance .

Mais formalmente, em C # v2.0 se T é um subtipo de U, então T [] é um subtipo de U [], mas G não é um subtipo de G (Onde G é qualquer tipo genérico). No terminologia teoria do tipo, descrevemos este comportamento dizendo que série C # tipos são “covariante” e genérico tipos são “invariável”.

Referência: http: // blogs. msdn.com/rmbyers/archive/2005/02/16/375079.aspx

Se você tem o seguinte método:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile

Onde como se você usar o tipo de restrição genérica, ele vai.

Para mais informações sobre Covariância e Contravariance], veja série de mensagens de Eric Lippert .


Outras mensagens vale a pena ler:

Outras dicas

Suponha que você tenha uma lista:

List<Stuff1> l = // get from somewhere

Agora tente:

AllDoSomething(l);

Com a versão genérica, será permitido. Com o não-genérico, ele não vai. Essa é a diferença essencial. Uma lista de Stuff1 não é uma lista de StuffBase. Mas, no caso genérico, você não exigem que ele seja exatamente uma lista de StuffBase, por isso é mais flexível.

Você poderia resolver isso primeiro copiar sua lista de Stuff1 em uma lista de StuffBase, para torná-lo compatível com a versão não-genérico. Mas, em seguida, suponha que você tinha um método:

List<T> TransformList<T>(List<T> input) where T : StuffBase
{
    List<T> output = new List<T>();

    foreach (T item in input)
    {
        // examine item and decide whether to discard it,
        // make new items, whatever
    }

    return output;
}

Sem genéricos, você poderia aceitar uma lista de StuffBase, mas você teria, então, para retornar uma lista de StuffBase. O chamador teria de usar moldes se soubessem que os itens foram realmente de um tipo derivado. Assim, os genéricos permitem preservar o tipo real de um argumento e canalizá-la através do método para o tipo de retorno.

No exemplo que você forneceu não há diferença, mas tente o seguinte:

List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);

A primeira chamada funciona bem, mas o segundo não compila devido a covariância genérico

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top