Can a list of a type not be a list of its 'top' type?

4

There are some entities in the application I'm developing that need to be sorted by a predefined routine.

Thinking about this has created a contract class called ElementoOrdenavel and all entities that can be ordered inherit from this class.

It exposes, a priori , only two members. Here is the class code.

public abstract class ElementoOrdenavel
{
    public int Id { get; set; }
    public int Ordem { get; set; }
}

Example of a class that inherits from this

public class Banner : ElementoOrdenavel
{
    public string Descricao { get; set; }
}

To facilitate my work, I've made a method that gets a list of ElementoOrdenavel and does the sorting work, so

void Reorganizar(IEnumerable<ElementoOrdenavel> colecao, int[] idsOrdenados)
{
    for (int ordem = 0; ordem < idsOrdenados.Length; ordem++)
    {
        var elemento = colecao.First(m => m.Id == idsOrdenados[ordem]);
        elemento.Ordem = ordem + 1;
    }
}

And I tried calling this method as well

var banners = db.Banners.AsEnumerable();
Reorganizar(banners, ids);

And, to my surprise, I received the error

  

Can not convert from ' IEnumerable<Banner> ' to ' IEnumerable<ElementoOrdenavel> '

A list of Banner not is a list of ElementoOrdenavel ?

By doing a generic method, it works normally. Because?

void Reorganizar<T>(IEnumerable<T> colecao, int[] idsOrdenados) where T : ElementoOrdenavel
{
    for (int ordem = 0; ordem < idsOrdenados.Length; ordem++)
    {
        var elemento = colecao.First(m => m.Id == idsOrdenados[ordem]);
        elemento.Ordem = ordem + 1;
    }
}
    
asked by anonymous 21.06.2017 / 21:05

1 answer

1

Covariance is the conversion of an object of the most specific type Banner into a more generic type ElementoOrdenavel . Operators can only be used in parameters of interfaces or delegates generic.

It could be done like this:

var banners = db.Banners.AsEnumerable();
IEnumerable<ElementoOrdenavel> elemento = banners;
Reorganizar(elemento , ids);

Or, if you use an interface to implement ElementoOrdenavel :

public interface IElementoOrdenavel
{
    int Id { get; set; }
    int Ordem { get; set; }
}
public abstract class ElementoOrdenavel: IElementoOrdenavel
{
    public int Id { get; set; }
    public int Ordem { get; set; }

}
public class Banner : ElementoOrdenavel
{
    public string Descricao { get; set; }
}

public class Teste
{
    public static void TesteReorganizar()
    {
        IEnumerable<Banner> a = new List<Banner>() { new Banner(){ Descricao="TESTE", Id = 1, Ordem = 0} };


        Reorganizar(a, new int[] { 1 });
    }

    public static void Reorganizar(IEnumerable<IElementoOrdenavel> colecao, int[] idsOrdenados)
    {

        for (int ordem = 0; ordem < idsOrdenados.Length; ordem++)
        {
            var elemento = colecao.First(m => m.Id == idsOrdenados[ordem]);
            elemento.Ordem = ordem + 1;
        }
    }
}

Or, since you do not like to use inheritance, remove the class ElementoOrdenavel and class Banner and others will directly inherit interface IElementoOrdenavel

Update:

I have refined your code, now at home, and to my surprise it worked normally. It was then that I decided to check out the .NET Framework version.

You should be using version 3.5, which presented the same error, in the higher versions it is possible to do this conversion directly, without any problem.

Searching the Interface documentation :

In version 3.5 or lower, the interface declaration looks like this:

public interface IEnumerable<T> : IEnumerable

Already from version 4.0, the statement looks like this:

public interface IEnumerable<out T> : IEnumerable
So the covariance defined by the out key was only implemented from .NET Framework version 4.0

    
21.06.2017 / 21:50