Alexandr
Alexandr

Reputation: 1901

Covariance in generic collections C#

I have a some classes and interface

public interface IGeoObject
    {
        GeoCoordinate GetGeocoordinate();
    }

public class User : IGeoObject
    {
        public GeoCoordinate GetGeocoordinate()
        {
            //...
            return GeoCoordinate;           
        }
    }  

public class Venue : IGeoObject
    {    
        public GeoCoordinate GetGeocoordinate()
        {
            //...
            return GeoCoordinate;
        }
    }

public class Place : IGeoObject
    {    
        public GeoCoordinate GetGeocoordinate()
        {
            //...
            return GeoCoordinate;
        }
    }

I have, example variable users with type List<User>

And I have method with signature

double GetMinDistance(List<IGeoObject> objects, GeoCoordinate position)

I can't pass users to GetMinDistance, because covariance of generic types not work.

I can create additional list and use method .ConvertAll (or .CasT<T>):

List<IGeoObject> list = new List<User>(listUser).ConvertAll(x => (IGeoObject)x);
var dist = GeoTest.GetMinDistance(list, position);

I'm not sure that this is the most elegant solution. Most embarrassing that the caller must do this conversion.

This is similar to my problems in the architecture? There are more elegant solutions?

P.S. Long story, but I really can not give the coordinates of the same species in all classes.

Upvotes: 1

Views: 733

Answers (3)

millimoose
millimoose

Reputation: 39950

You can change the signature to:

double GetMinDistance<TGeoObject>(List<TGeoObject> objects, GeoCoordinate position)
    where TGeoObject : IGeoObject 
{
    // ...
}

Upvotes: 3

MarcinJuraszek
MarcinJuraszek

Reputation: 125620

That's because List<T> is not covariant.

public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable

As you can see, there is no out keyword in class declaration.

You have to use IEnumerable<T> instead to get covariance support.

public interface IEnumerable<out T> : IEnumerable

That's because List<T> has a set of methods that allows you modifying the collection, and with covariance you would lose compile type safety of these operations. IEnumerable<T> only allows you to iterate over the collection and that's why it can be marked as covariant.

Upvotes: 8

Justin Lessard
Justin Lessard

Reputation: 11901

You can define a explicit (cast) or an implicit operator that take an List<User> and convert it to a List<IGeoObject>.

public static explicit operator List<IGeoObject>(List<IGeoObject> ls)
{
  return new ls.ConvertAll(x => (IGeoObject)x);
}

Upvotes: 1

Related Questions