Как переписать функцию generi c в C# в OOP, чтобы избежать переключения типов? - PullRequest
1 голос
/ 06 апреля 2020

У меня есть клиент, который отправляет запросы на сервер. Запросы и ответы также являются игровыми событиями, поэтому я могу отправлять запросы и ответы туда, где они нужны в коде:

public class Message {}

public interface IOnlineRequest : IGameEvent {}

public interface IOnlineResponse : IGameEvent {}

public abstract class OnlineRequest<T> : Message, IOnlineRequest {
    public abstract T Request { get; set; }
}

public abstract class OnlineResponse<T> : Message, IOnlineResponse {
    public abstract T Response { get; set; }
}

public sealed class OPingRequest : OnlineRequest<PingRequest>
{
    public override PingRequest Request { get; set; }
}

public sealed class OPingResponse : OnlineResponse<PingResponse>
{
    public override PingResponse Response { get; set; }
}

Существует класс менеджера, который имеет следующие члены:

private SortedDictionary<int, IOnlineRequest> requests_ = new SortedDictionary<int, IOnlineRequest>();
private SortedDictionary<int, IOnlineResponse> responses_ = new SortedDictionary<int, IOnlineResponse>();

Я повторяю запросы и включаю тип (эта часть мне не нравится, и я хотел бы переписать ее с помощью OOP):

bool error = false;
foreach (var request in requests_)
{
    if (!error)
    {
        switch (request.Value)
        {
            case OPingRequest req:
                error = await ProcessRequest<PingRequest, PingResponse, OPingResponse>(request.Key, req, Profile.PingAsync);
                break;
            case OKeepAliveRequest req:
                error = await ProcessRequest<KeepAliveRequest, KeepAliveResponse, OKeepAliveResponse>(request.Key, req, Profile.KeepAliveAsync);
                break;
            case OLoginRequest req:
                error = await ProcessRequest<LoginRequest, LoginResponse, OLoginResponse>(request.Key, req, Profile.LoginAsync);
                break;
            case OCounterRequest req:
                error = await ProcessRequest<CounterRequest, CounterResponse, OCounterResponse>(request.Key, req, Profile.CounterAsync);
                break;
            default:
                throw new NotImplementedException("Add code for your request type above!");
        }
    }
    else
    {
        break;  // Break out of foreach
    }
}

И это обобщение c Функция ProcessRequest:

private async Task<bool> ProcessRequest<T, U, V>(int num, OnlineRequest<T> req, Func<T, CancellationToken, Task<U>> func)
    where T : class, IMessage<T>
    where U : class, IMessage<U>
    where V : OnlineResponse<U>, new()
{
    req.State = new RequestState();
    U resp = null;

    try
    {
        resp = await func(req.Request);

        req.State.Status = RequestStatus.Success;
        req.State.Message = "";
    }
    catch (Exception ex)
    {
        req.State.Status = RequestStatus.Failed;
        req.State.Message = ex.ToString();
    }

    responses_.Add(num, new V { State = req.State, Response = resp });

    return true;
}

Трудно повторно реализовать это в OOP, в основном потому, что такие типы, как PingRequest, PingResponse, являются конкретными типами, и они генерируются, поэтому у меня нет выбора и я не могу измените их.

Я бы хотел добиться:

bool error = false;
foreach (var request in requests_)
{
    if (!error)
    {
        error = request.Process();
    }
    else
    {
        break;
    }
}

1 Ответ

0 голосов
/ 06 апреля 2020

в основном потому, что типы, такие как PingRequest, PingResponse, являются конкретными типами, и они генерируются, поэтому у меня нет выбора, и я не могу их изменить

Большинство инструментов генератора кода имеют общую порядочность: объявлять типы через partial class. Это означает, что вы можете создать отдельный файл кода для добавления вещей без редактирования сгенерированных файлов , например, реализацию интерфейса:

namespace TheSameNamespaceAsBefore {
    partial class PingRequest : IMyInterface {
        bool IMyInterface.DoTheThing() { /* your code here */ }
    }
    partial class PingResponse : IMyInterface {
        bool IMyInterface.DoTheThing() { /* your code here */ }
    }
}

Теперь вы можете просто сделайте что-то вроде:

if (request.Value is IMyInterface foo) { error = foo.DoTheThing(); }
else { /* warn? throw? set error to true? break? */ }

Вы также можете использовать общий базовый тип, но здесь интерфейс выглядит хорошо.

(иногда генератор кода также генерирует «частичные методы» - заглушки, которые вы можете при желании реализовать в своем частичном файле классов, чтобы обеспечить функциональность, вызываемую в указанных точках c в сгенерированном коде; если вы не реализуете их, вызовы просто испаряются во время компиляции, как если бы они никогда не существовали)

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