-1

I tried buiding a game made with Unity by building objects I called WorldObjects. WorldObjects are spawned via a data class (WorldObjectData) that is governed by a manager (WorldObjectDataManager). The part that needs fixing is the process that spawns WorldObjects. Long story short, I have to pass two generic parameters to a WorldObject builder: the first one is the type of the WorldObject, the second one is the type of data class (so that the script can fetch the correct WorldObjectDataManager).

Error CS0029 Cannot implicitly convert type 'Sandbox.GUI.Sandbox.BuildButton.BuildMenuEntry<Sandbox.Selectables.Entities.JobInteractables.WorldObjects.Consoles.Console, Sandbox.Selectables.Entities.JobInteractables.WorldObjects.Consoles.ConsoleData>' to 'Sandbox.GUI.Sandbox.BuildButton.BuildMenuEntry<T, G>' Assembly-CSharp C:\Unity Projects\80s Sandbox\Assets\Scripts\GUI\Sandbox\BuildButtons\BuildButtonsChildren\BuildControl.cs 16 Active

This is the error I get when I try to compile line Console.GetEntry():

using Sandbox.Selectables.Entities.JobInteractables.WorldObjects;
using Sandbox.Selectables.Entities.JobInteractables.WorldObjects.Consoles;

namespace Sandbox.GUI.Sandbox.BuildButton // Used to spawn WorldObjects via a button
{
    /// <summary>
    /// BuildButton used for control buildings (e.g.: consoles)
    /// </summary>
    public class BuildControl<T, G> : CreateBuildButtonNew<T, G> where T : WorldObject where G : WorldObjectData
    {
        protected override BuildMenuEntry<T, G>[] GetBuildMenuEntries()
        {
            return new BuildMenuEntry<T, G>[]
            {
                Console.GetEntry()
            };
        }
    }
} 

Console.GetEntry is coded as follows:

public static BuildMenuEntry<Console, ConsoleData> GetEntry()
        {
            return new BuildMenuEntry<Console, ConsoleData> // Console and ConsoleData inherit from WorldObject and WorldObjectData respectively
            {
                entryName = UIStringID, // string
                entrysMaterials = new WorldObjectMaterial[] { WorldObjectMaterial.Steel }, // WorldObjectMaterial is an enum
                associatedFunction = InitializeByStatic<Console, ConsoleData> // the spawning function
            };
        }

Finally, BuildMenuEntry:

using Sandbox.Selectables.Entities.JobInteractables.WorldObjects;

namespace Sandbox.GUI.Sandbox.BuildButton
{
    public struct BuildMenuEntry<T, G> where T : WorldObject where G : WorldObjectData
    {
        public string entryName;
        public WorldObjectMaterial[] entrysMaterials;
        public SpawnWorldObject<T, G> associatedFunction;
    }
}

I have no idea what's the problem here since T and G are constrained to be respectively WorldObject and WorldObjectData, and Console.GetEntry returns a class whose generic types inherit from WorldObject and WorldObjectData.

Any help is greatly appreciated!

jetFire55
  • 49
  • 5
  • I believe it's because `GetEntry` requires a more derived type than `BuildControl` requires. If you constrain `BuildControl` to `Console` and `ConsoleData`, does the error go away? – Rufus L Sep 11 '20 at 19:35
  • _"Cannot implicitly convert type to generic type when generic type is parent of type"_ -- your premise is flawed. You believe that the generic types involved have a parent (base)/child (derived) relationship when in fact they do not. Only the _type parameters_ do. But having type parameters that are base & child do not make the generic types themselves base & child. See duplicate. – Peter Duniho Sep 12 '20 at 00:08
  • @RufusL Yes, once I constrain BuildControl to Console and ConsoleData the error goes away – jetFire55 Sep 12 '20 at 07:30

1 Answers1

0

The problem is that you're trying to put a BuildMenuEntry<Console, ConsoleData> inside an array of BuildMenuEntry<T, G>.


Here's a situation that will help understand the issue.

Your class BuildControl<T, G> is generic. Let's imagine we use it as a BuildControl<Car, CarData>. In this case, your method can be read like this:

protected override BuildMenuEntry<Car, CarData>[] GetBuildMenuEntries()
{
    return new BuildMenuEntry<Car, CarData>[]
    {
        Console.GetEntry()
    };
}

Now we can see that Consol.GetEntry returns a BuildMenuEntry<Console, ConsoleData>. You can't put a BuildMenuEntry<Console, ConsoleData> inside an array of BuildMenuEntry<Car, CarData> because Console isn't Car and ConsoleData isn't CarData.

What you put in the array of BuildMenuEntry<T, G> must be BuildMenuEntry<T, G> items, where T and G are still generics, not something concrete.

Batesias
  • 1,914
  • 1
  • 12
  • 22