Хочу упростить код, но для этого нужно использовать reflections. Можно по другому?

Делаю автоматическое создание ставок на исход игры. после игры получаю объект матча, в котором содержится статистика по убийствам, заработанному золоту и т.д. Во время начала матча ставка выбирается рандомно, в зависимости от того какая ставка выбирается, вызывается соответствующий асинхронный метод, который запускает саму ставку, ждет конца матча и подсчитывает результаты. Проблема в том что у меня с дюжину такие методов с идентичным кодом, разница лишь в том какая переменная объекта используется для подсчета результата. Я могу использовать reflection и передавать в метод string с названием переменной, но говорят что reflection это турбо плохо. можно это реализовать как-то по другому?

Thread riotMatchesTask = new Thread(() => GetCurrentMatchTask().Wait()); 
async Task GetCurrentMatchTask()
{
    while (true)
    {
        try
        {
            //Пока не в игре, проверяем стартанула ли игра. Когда стартанула рандомно 
            //выбираем ставку и запускаем ее
            var CurrentGame = await api.Spectator.GetCurrentGameAsync();
            bool procked = false;
            if (procked == false && getchance(20))
            {
                await Prediction_MAX_Kills(CurrentGame).ConfigureAwait(false);
                procked = true;
            }
            if (procked == false && getchance(20))
            {
                await Prediction_MAX_gold(CurrentGame).ConfigureAwait(false);
                procked = true;
            }
            //и т.д. на каждую ставку свой метод                
            await Task.Delay(2000).ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            if (!ex.InnerException.Message.Contains("Data not found")) //Ожидаемо                                          
                _logWriter.LogWriterTask(ex, "GetCurrentMatchTask_1");
        }
    }
}
 async Task Prediction_MAX_gold(SpectatorEndpoint.CurrentGame CurrentGame)
{
  //запуск ставки
  ...
  //ожидание конца игры
  ...
  //получаю данные об исходе игры
  var onMatch = await api.Match.GetMatchAsync(CurrentGame.currentGameID);
  var FinParticipants = onMatch.Info.Participants.ToArray(); //получаю игроков
  
  foreach (var Participant in FinParticipants)  //для каждого игрока формирую статистику по параметру
  {
     var vFlag = Participant.GoldEarned; //!!!параметр
     Players.Add(new cPlayersObject.PlayersObject()
     {
        name = Participant.SummonerName,
        champ = Participant.ChampionName,
        Flag = vFlag
        });
    }
}

Каждый метод Prediction_MAX_"что-то" это идентичный код, за исключение строчки с параметром. если я сделаю метод с reflection

foreach (var Participant in FinParticipants)
{
var val = Participant.GetType().GetProperty(_Flag).GetValue(Participant, null);
var vFlag = (long)Convert.ChangeType(val, typeof(long));
Players.Add(new cPlayersObject.PlayersObject()
{
    name = Participant.SummonerName,
    champ = Participant.ChampionName,
    Flag = vFlag
});
}

и буду в него передавать string _Flag с названием параметра, это удалит лишние 3000 строк кода. Как можно сделать тоже самое, но без reflection?


Ответы (1 шт):

Автор решения: Alexander Petrov

Добавляем в метод Prediction_MAX параметр-делегат, который принимает тип Participant и возвращает значение того же типа, что и Flag. Внутри метода этот делегат вызывается.

async Task Prediction_MAX(... CurrentGame, Func<Participant, long> func)
{
    ...

    foreach (var Participant in FinParticipants)
    {
        Players.Add(new cPlayersObject.PlayersObject()
        {
            ...
            Flag = func(Participant)
        });
    }
}

Вызываем этот метод следующим образом:

await Prediction_MAX(CurrentGame, participant => participant.Kills)

Можно параметр именовать покороче:

await Prediction_MAX(CurrentGame, p => p.GoldEarned)

Я бы порекомендовал исправить нейминг на общепринятый.
PascalCase для имён свойств и методов, camelCase для локальных переменных и параметров.
Очень уж глаз цепляется.

→ Ссылка