Авто приведение абстрактных классов в c# - PullRequest
0 голосов
/ 19 апреля 2020

Я пытаюсь разобраться в создании упрощенного способа создания элементов, списков и баз данных в C# И хотя все работает, мне потребовалось изгнать результаты.

Пока что У меня есть следующее

namespace Game.Database
{

    public abstract class DatabaseItem : ScriptableObject
    {

    }

    public class DatabaseList : ScriptableObject
    {
        public List<DatabaseItem> items;
    }

    public class ShipClass : ScriptableObject
    {
        public string shipClassID;
        new public string name;
    }

    public class Ship : DatabaseItem
    {
        public string shipID;
        new public string name;
        public ScriptableObject shipClass;
    }

}

public class Database : MonoBehaviour
{
    public List<DatabaseList> lists;

     void Start()
    {
        Ship ship = (Ship)lists[0].items[0];
        Debug.Log(shipClass.shipID);
        ShipClass shipClass = (ShipClass)ship.shipClass;
        Debug.Log(shipClass.shipClassID);
    }
}

Имейте в виду, что это проект единства, поэтому эти элементы создаются и данные назначаются через пользовательский интерфейс.

Как вы можете видеть, у меня есть резюме для мои предметы и будут иметь несколько типов предметов и несколько списков. Я пытаюсь избежать создания нескольких классов для моих списков, по одному для каждого типа элементов. Итак, я абстрагировал свои элементы от DatabaseItem, чтобы я мог сохранить список DatabaseItem в моем DatabaseList. Однако это означает, что при чтении моих данных мне нужно преобразовать их обратно в класс Ship.

Хотя это не плохо для простой реализации, в производстве они будут вложенными, требуя многократных приведений, чтобы перейти к необходимые данные.

К сожалению, мне не хватает необходимого c# словаря, чтобы по-настоящему решить проблему. Глядя на определяемые пользователем операторы преобразования Microsoft и их пример, просто не имеет смысла, если это даже то, чего я хочу достичь.

РЕДАКТИРОВАТЬ - Проблема, если я не могу получить доступ к данным, поскольку я могу это сделать, это необходимость разбивать каждый уровень данных, так как в итоге это будет очень обобщенно c, используемое для всех игровых данных и очень вложенное, поэтому я пытаюсь разбить каждый уровень, чтобы иметь возможность разбить его на части. чтобы избежать.

Ответы [ 2 ]

1 голос
/ 19 апреля 2020

Один из способов - показать метод для каждого типа элемента, который выполняет запись данных, поэтому вызывающему коду не нужно знать подробности низкого уровня.

См. Ниже о том, как избежать выполнения приведения.

namespace Game.Database
{

    public abstract class DatabaseItem : ScriptableObject
    {
         public abstract void WriteOut();
    }

    public class DatabaseList : ScriptableObject
    {
        public List<DatabaseItem> items;
    }

    public class Ship : DatabaseItem
    {
        public string shipID;
        new public string name;

        public override void WriteOut()
        {
            Debug.Log(shipID);
        }
    }

}

public class Database : MonoBehaviour
{
    public List<DatabaseList> lists;

     void Start()
     {
        lists[0].items[0].WriteOut();
     }
}

Таким образом, вы разрешаете каждому типу элемента обрабатывать свои записи. Я бы посоветовал тщательно продумать свой API.

Чтобы быть еще более SOLID чистым, вы могли бы использовать внедрение зависимостей и внедрить запись в тип, другой пример приведен ниже.

Это имеет преимущество, позволяющее нескольким типам использовать один и тот же код писателя, и вы также поддерживаете свой класс в соответствии с принципом единой ответственности.

namespace Game.Database
{
    public interface IWriter
    {
        void Write(string output);
    }

    public class ConsoleWriter: IWriter
    {
        public void Write(string output)
        {
             Debug.Log(output);
        }
    }

    public abstract class DatabaseItem : ScriptableObject
    {
         public abstract void WriteOut();
    }

    public class DatabaseList : ScriptableObject
    {
        public List<DatabaseItem> items;
    }

    public class Ship : DatabaseItem
    {
        private IWriter _writer;

        public Ship(IWriter writer)
        {
            _writer = writer;
        }

        public string shipID;
        new public string name;

        public override void WriteOut()
        {
            _writer.Write(shipID);
        }
    }
}

public class Database : MonoBehaviour
{
    public List<DatabaseList> lists;

     void Start()
     {
        lists[0].items[0].WriteOut();
     }
}
0 голосов
/ 19 апреля 2020

Примерно так должно быть хорошей отправной точкой. Указанные c реализации должны быть адаптированы к потребностям вашей игры. Больше информации о дженери c типах здесь .

public abstract class Item: ScriptableObject
{
   public string name;

   public abstract void Use();
}

public class Ship: Item
{
   public string id;

   public override void Use()
   {
      Debug.Log($"I'm a Ship, my name is {name}, my id is {id}.");
   }
}

public class Plane: Item
{
   public float speed;

   public override void Use()
   {
      Debug.Log($"I'm a Plane, my name is {name}, my speed is {speed}.");
   }
}

public class Database: ScriptableObject
{
   [SerializeField] private List<Item> items;

   public T GetItem<T>(int i) { return (T) items[i]; }

   public Item AddItem() { ... }
   public Item RemoveItem() { ... }
}

public class DatabaseHolder: MonoBehaviour
{
   public Database database;

   void Start()
   {
      Ship ship = database.GetItem<Ship>(0);

      // Or...

      Plane plane = database.GetItem<Plane>(1);
   }
}
...