4

I need help understanding the use of abstract classes. I have an abstract class called WorldObject:

public abstract class WorldObject
{
    public Vector3 position;
}

I have many classes that inherit from this, for example Building and Tree.

I want to create a method that loops through a List of WorldObjects, and return the closest one to me. I want to be able to use this method with all my types of WorldObjects, but I can't get it to work:

public List<Building> buildings = new List<Building>();
public List<Tree> trees= new List<Tree>();

private void GoToClosestBuilding()
{
    var target = GetClosestWorldObject(buildings); //Error: Cannot convert building to WorldObject
}

Vector3 GetClosestWorldObject(List<WorldObject> objects)
{
    //...
}

When I try to pass my Building or Tree lists to my method, it returns an error that it cannot convert between Building and WorldObject, and so on.

How can I structure my code so that something like this would work?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Green_qaue
  • 3,561
  • 11
  • 47
  • 89

2 Answers2

6

A List<Building> is not assignment compatible to a List<WorldObject> because then you could do

List<WorldObject> worldObjects = buildings; // Not possible!
worldObjects.Add(tree); // Legal

worldObjects.Add(tree); is perfectly legal. But as worldObjects would reference a list of buildings, you would not be allowed to add it a tree!

However, you can change the signature of the method to

Vector3 GetClosestWorldObject(IEnumerable<WorldObject> objects)
{
    ...
}

Now, this works

var target = GetClosestWorldObject(buildings);

because the enumerable is declared as

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

The out keyword states that T occurs only in out-positions, i.e. as return value or as out parameter. This makes the type co-variant.

See also: - Covariance and Contravariance (C#) - Microsoft Docs

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
1

You can use a generic function.

Vector3 GetClosestWorldObject<T>(List<T> objects) where T: WorldObject 
{
    // ...
}

The problem, as mentioned by other answers, is that a List<WorldObject> is not the same as a List<Building>. If you want to pass a List into your argument and not an IEnumerable, you can use a generic function that takes a List of T where T is a WorldObject. Now that T is assigned to Building, List<Building> is acceptable.

dx_over_dt
  • 13,240
  • 17
  • 54
  • 102