Есть ли способ получить аргумент типа из строки для передачи в универсальный метод? - PullRequest
0 голосов
/ 04 ноября 2019

Я набрал это в Notepad ++ очень быстро, поэтому, пожалуйста, прости любые опечатки / ошибки. Если бы это было возможно, я бы избавился от некоторой повторяющейся работы (то есть длинного случая). Ничего страшного, но мне любопытно, если это возможно, и если да, то как плохо было бы на самом деле реализовать код.

jsonFromWebpage = {
    StatusUpdate: {
        QueryType: "SomeClassName",
        LocalCount: 5,
        RemoteCount: 5
    },
    StatusUpdate: {
        QueryType: "AnotherClass",
        LocalCount: 29,
        RemoteCount: 30
    }
}

// Model
public class StatusUpdate
{
    public string QueryType { get; set; }
    public int LocalCount { get; set; }
    public int RemoteCount { get; set; }
}

// Controller
public IActionResult GetStatusUpdate([FromBody] List<StatusUpdate> status)
{
    _service.GetStatusUpdate(status);
    return status
}

// Service
public List<Status> GetStatusUpdate(List<StatusUpdate> status)
{
    foreach(var s in status)
    {
        var typeArgument = s.QueryType; // <--- Is there a way for this...
        status.CurrentCount = GetTotalCount<typeArgument>(); // <--- to work here?
        status.RemoteCount = thisworksfineforotherreasons(s.QueryType);
    }
}

// Repo
public int GetTotalCount<T>() where T: class
{
    var result = _db.GetCount<T>();
    return result;
}

РЕДАКТИРОВАТЬ

Во-первых, спасибо всем, кто ответил. Прочитав все до сих пор, я хотел дать немного больше контекста. Вот другой пример на примере:

// View
<div class="col-12">
    <div class="api-types">History</div>
    <div class="progress-bar">50 out of 50 copied</div>
</div>
<div class="col-12">
    <div class="api-types">Users</div>
    <div class="progress-bar">25 out of 32 copied</div>
</div>

// -- View javascript

var types = [];
$(".api-types").each(function (c, i) {
    types.push({ ApiAndClassName: $(i).text() });
});

pushToController(JSON.stringify(types));

// Controller
public IActionResult GetSyncStatus(List<SyncStatusVM> status)
{
    _service.GetSyncStatus(status);
    return Json(status);
}

// Service
public List<SyncStatusVM> GetSyncStatus(List<SyncStatusVM> status)
{
    foreach(var s in status)
    {
        // LocalCount
        var magicTypeFigurator = s.ApiAndClassName
        s.LocalCount = _repo.GetCount<magicTypeFigurator>(); <-- "this is a variable but should be a type..."

        // Remote
        var url = $"https://api.domain.com/{s.ApiAndClassName.ToLower()}"
        s.RemoteCount = FetchCountFromApi(url);
    }
    return status;
}

// Repository
public long GetCount<T>()
{
    var result = _orm.Count<T>();
    return result;
}

// Models
public class SyncStatusVM
{
    public string ApiAndClassName { get; set; }
    public int LocalCount { get; set; }
    public int RemoteCount { get; set; }
}

public class History
{
    public long Id {get;set;}
    public DateTime CreatedDate {get;set;}
    public string Message {get;set;}
}

public class Users
{
    public long Id {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
}

Используя этот код, я могу просто создать раздел в представлении и класс для каждого типа. Класс повторно используется ORM и десериализуется из API. Наиболее громоздким моментом является наличие в контроллере оператора case, который вызывает универсальный метод с правильным типом, основанным на «ApiAndClassName». Я мог бы отредактировать ORM так, чтобы он основывался на строках, а не на общем, но мне не нравится этот метод по разным причинам. Я мог бы превратить оператор case в коллекцию в контроллере или просто переместить его на уровень обслуживания, но то, что у меня есть, уже работает. Я также мог бы просто сделать рефакторинг, чтобы представление строилось из коллекции, но есть и другие точки данных, где это не лучший вариант. Если нет чего-то, что я упускаю, родовой аргумент из строки вещь вроде как имеет смысл. Это крайний случай ... и просто любопытно, можно ли сделать это достаточно хорошо.

Ответы [ 2 ]

1 голос
/ 04 ноября 2019

Вообще сильная типизация - твой друг. Проверка типа времени компиляции - это особенность, а не враг, с которым нужно бороться. Без них или со слишком агрессивным приведением типов мы получаем примеры JavaScript и PHP из этого комикса .

Для работы со слабо типизированными языками или веб-сервисами .NET имеет ExpandoObject. Данные могут быть сохранены в нем, а затем перенесены в соответствующий тип экземпляра. Также похоже, что ваш случай попадет в десериализацию JSON, который является хорошо установленным кодом.

Универсальный - это неправильный термин. Обобщения обычно относятся к типу, который все еще известен во время компиляции, поэтому проверки типов во время компиляции все еще работают. Вы явно о типе, неизвестном во время компиляции, только во время выполнения. Это очень отличается от общего. Динамические типы являются подходящим термином afaik. Но чтобы не перепутать это с типом Dynamic (да, названия здесь становятся действительно запутанными).

Reflection - это тот дроид, которого вы ищете. Для большинства целей имя класса или поля не существует во время выполнения. Это прежде всего для вас и компилятора для общения. Теперь Reflection является исключением. Это все о получении вещей (например, экземпляры или свойства / поля) на основе строкового представления их имени. Необходимые метаданные включаются в сборки .NET, так же как и поддержка COM. Но так как я поддерживаю строгую типизацию, я не являюсь ее другом.

операторы switch / case обычно могут быть заменены некоторой коллекцией. Случаи действительно просто жестко закодированный способ проверить набор констант. Вы используете идентификатор регистра в качестве ключа и все, что вам нужно для значения. Вы можете полностью использовать функции в качестве значения (спасибо делегатам). Или тип Тип , который вы затем используете для создания экземпляра.

Но для вашего случая кажется, что все это неправильно. Bog standart Inheritance - Наследование может быть настоящим дроидом, которого вы ищете. Служба JSON обычно не дает вам другой экземпляр в одной коллекции, , если эти экземпляры не связаны каким-либо образом. «SomeClassName» и «AnotherClass» должны иметь другого предка. Или на самом деле они должны даже быть только одним классом - QueryType - это просто строковое поле указанного класса.

0 голосов
/ 04 ноября 2019

Предполагая, что у вас есть способ сопоставить строки с Type объектами, да: вы можете использовать MethodInfo.MakeGenericMethod():

var totalCount = (int) (
    GetType()
    .GetMethod("GetTotalCount")
    .MakeGenericMethod(MapStringToType(s.QueryType))
    .Invoke(this, null)
);

Это предполагает наличие метода Type MapStringToType(string) в локальной области видимости.

Один из способов отображения типов состоит в использовании Dictionary<string, Type> и заполнении его разрешенными типами и их соответствующими именами, которые будут использоваться в данных JSON для обращения к ним.

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