Универсальный абстрактный метод

#c# #generics #abstraction

#c# #обобщения #абстракция

Вопрос:

Я столкнулся с проблемой при попытке создать абстрактный класс и метод в нем, который был бы универсальным по своей природе.

 class GameRoomManager : MonoBehaviour {
    public GameRoom GetSomething(string s){
        //do things here
        return GameRoomvar;
    }
}
  

Теперь у меня есть другой класс, который делает нечто подобное, но задействованы другие классы

 class PlayerManager : MonoBehaviour{
    public Player GetSomething(string s){
        //player related things here
        return Playervar;
    }
}
  

Я хочу, чтобы оба класса GameRoomManager и PlayerManager наследовались от абстрактного класса Abs

 class GameRoomManager : Abs{
    public override GameRoom GetSomething<GameRoom>(string s){
        return GameRoomvar;
    }
}
  

где

 public abstract class Abs{
    public T GetSomething<T>(string s);
}
  

Я видел несколько ответов на эту тему, когда искал решения, и все предлагали, чтобы сам абстрактный класс был универсальным. Я не хочу делать абстрактный класс универсальным, поскольку примеры, которые я видел, заставили бы меня это сделать class GameRoomManager : Abs<GameRoomManager> . Но я хочу, чтобы метод возвращал тип GameRoom, а не Gameroocommanager.

Я не совсем знаком с обобщениями, поэтому, пожалуйста, укажите мне правильное направление, если я ошибаюсь

Комментарии:

1. But I want the method to return type PQR, not XYZ Итак, затем создайте PQR общий аргумент. Почему общим аргументом должен быть сам тип?

2. Да, я думал об этом. Но этот пример предназначен только для одного типа метода, который возвращает T. Что теперь, если у меня есть другой метод, который возвращает тип U? Я должен был бы сделать GG<T, U> , что кажется мне довольно глупым. С таким же успехом можно вообще не создавать абстрактный класс, если уж на то пошло. Или есть другая причина или какое-то лучшее объяснение, почему и как я должен сделать это так, как вы говорите?

3. Если вам не нужен абстрактный класс, то не создавайте абстрактный класс. Учитывая, что в нем нет реализованных методов, это действительно должен быть интерфейс, а не абстрактный класс, но даже тогда, если вам не нужен интерфейс, не создавайте его. Если он вам нужен, то используйте его.

4. Да, это имеет смысл. Что касается параметров типа, я предполагаю, что другого способа нет <T,U> ?

5. Пример кода слишком расплывчатый, чтобы понять, чего вы действительно пытаетесь здесь достичь. Не уверен, что это проблема XY или что. Для меня нет смысла использовать дженерики таким образом. Все, чего вы достигли, — это наличие двух классов с методом, который имеет одно и то же имя, но ведет себя по-разному. Если HIJ и PQR унаследованы от общей базы, тогда это имело бы больше смысла. Но тогда вам также не понадобились бы там обобщения.

Ответ №1:

У вас должно быть что-то общее с PQR и HIJ для того, чтобы классы использовали общий метод.

Планируйте

Соединяйте вещи с интерфейсами.

 public interface IPart
{
    // put things here that are common between Part and GameRoom
    int ID { get; }
}
public interface IAbs
{
    IPart GetSomething(string name);
}

public class GameRoom : IPart
{
    public int ID { get; set; }
}

public class GameRoomManager : IAbs
{
    GameRoom part;

    #region IAbs Members

    public GameRoom GetSomething(string name)
    {
        return part;
    }


    IPart IAbs.GetSomething(string name)
    {
        return GetSomething(name);
    }

    #endregion
}

public class Player : IPart
{
    public int ID { get; set; }
}

public class PlayerManager : IAbs
{
    Player part;


    #region IAbs Members

    public Player GetSomething(string name)
    {
        return part;
    }
    IPart IAbs.GetSomething(string name)
    {
        return GetSomething(name);
    }

    #endregion
}
  

План B

Используйте базовый класс с универсальным типом и интерфейсами

 public interface IItem
{
    // put things here that are common between Part and GameRoom
    int ID { get; }
}
public class GameRoom : IItem
{
    public int ID { get; set; }
}
public class Player : IItem
{
    public int ID { get; set; }
}


public interface IAbs
{
    IItem GetItem(string guid);
}
public abstract class Abs<T> : IAbs
    where T : IItem
{
    protected abstract T GetItem(string name);

    protected Abs(T item)
    {
        this.Item=item;
    }
    protected T Item { get; private set; }

    #region IAbs Members

    IItem IAbs.GetItem(string name)
    {
        return GetItem(name);
    }

    #endregion
}


public class GameRoomManager : Abs<GameRoom>
{
    public GameRoomManager(GameRoom room) : base(room)
    {
    }
    protected override GameRoom GetItem(string guid)
    {
        return Item;
    }
    public GameRoom GetRoom(string guid) { return GetItem(guid); }
}

public class PlayerManager : Abs<Player>
{
    public PlayerManager(Player player)
        : base(player)
    {
    }
    protected override Player GetItem(string guid)
    {
        return Item;
    }
    public Player GetPlayer(string guid) { return GetItem(guid); }
}
  

вот несколько примеров использования:

 class Program
{
    static void Main(string[] args)
    {

        List<IAbs> managers=new List<IAbs>();
        var pm=new PlayerManager(new Player() { ID=1001 });
        var gm=new GameRoomManager(new GameRoom() { ID=2050 });

        managers.Add(pm);
        managers.Add(gm);

        IItem part = managers[0].GetItem("0000");

    }
}
  

Комментарии:

1. Обновил вопрос примерно тогда, когда вы опубликовали свой ответ. Добавлены имена классов для вас, чтобы придать ему больше смысла

2. и я обновил ответ. Пожалуйста, прокомментируйте, как этот подход может не соответствовать вашим требованиям, потому что мне неясно, к чему вы стремитесь.

3. Хорошо, итак, чтобы быть еще более конкретным, getSomething будет GetRoom(строковый идентификатор guid), который вернет объект типа GameRoom, и то же самое для player, это будет GetPlayer (строковый идентификатор guid), который возвращает Player