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

У меня есть набор методов, которые просто возвращают сохраненный «объект» как каждый из разных типов, например:

bool GetAsBool();
string GetAsString();
int GetAsInt();

В реальном сценарии на данный момент их 16, а имена методов немного длиннее, что делает их неудобными для чтения. Теперь я хотел сделать это более удобным, определив один общий метод, который выбирает правильный метод из этого набора методов «Get». Это обеспечит подсветку синтаксиса для типа, передаваемого через обобщенный тип, и сделает имя короче, что, как мне кажется, в конечном итоге будет легче читать. Но я не могу понять, как выполнить преобразование типов.

Общий метод в настоящее время выглядит примерно так:

T Get<T>()
{
    var returnTpe = typeof(T);
    if (returnType == typeof(bool) { return GetAsBool(); }
    else if (returnType == typeof(string) { return GetAsString(); }
    else if (returnType == typeof(int) { return GetAsInt(); }
    else { return default; }
}

Это дает мне Cannot implicitly convert type 'bool' to 'T' (для bool, аналогично для других типов). Но из предложения if я знаю, какого типа будет T к тому времени, как я туда доберусь. Таким образом, тип «T» и тип возвращаемого значения метода, который я выбираю, всегда совпадают. Это должно означать, что преобразование всегда возможно, верно? Как мне преобразовать набранный результат обратно в T?

Пока что я пробовал только простой приведение через return (T)GetAsBool(), что дает мне Cannot convert type 'bool' to 'T'. Я не эксперт в отношении типов или дженериков, и поиск в Google больше не привел к появлению каких-либо идей с моей стороны. Возможно ли то, что я пытаюсь сделать?

return default здесь не следует использовать (поскольку он не позволяет потребителям обнаружить ошибочные состояния). Вместо этого вам следует создать исключение или использовать метод в стиле Boolean TryGet( out T value ).
Dai 06.06.2024 09:02

Какая польза от этого по сравнению с просто разоблачением GetAsString и т. д.? Но если вы действительно этого хотите, проверьте dotnetfiddle.net/MpXwvz .

mjwills 06.06.2024 09:03

Ошибка «Невозможно преобразовать тип «bool» в «T» связана с тем, что вы смешиваете типы значений и ссылочные типы в одном универсальном методе. Одним из обходных путей является применение преобразования упаковки ко всем типам значений путем выполнения промежуточного приведения к Object, но это неэффективно, поскольку приводит к выделению кучи.

Dai 06.06.2024 09:05

Пожалуйста, не делитесь кодом с таким количеством очевидных опечаток.

mjwills 06.06.2024 09:05

Вы неправильно понимаете, как работают дженерики. Чтобы вызвать универсальный метод, вам все равно необходимо знать универсальный тип при написании кода для вызова метода. Он не откладывается до времени выполнения. Вам все равно придется написать что-то вроде var str = Get<string>();, так что в этом нет никакого преимущества перед var str = GetAsString();. Смысл обобщений состоит в том, чтобы дать вам возможность написать код один раз и заставить его работать для нескольких разных типов, а не откладывать проверку типов до времени выполнения.

jmcilhinney 06.06.2024 09:05

@Dai Это не имеет ничего общего с боксом. Попытка привести ссылочный тип к T без какого-либо общего ограничения приведет к той же проблеме. Это потому, что компилятор не может гарантировать безопасное приведение к T.

Johnathan Barclay 06.06.2024 09:48

@jmcilhinney Может быть полезно, если Get<T> часто вызывается в других универсальных методах. Это предотвратит проведение этих проверок типов в нескольких местах.

Johnathan Barclay 06.06.2024 09:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы получить то, что вы просите, вы можете выполнить двойное приведение сначала к object, а затем к T, и оно скомпилируется. Я не уверен, насколько это эффективно для структур, поскольку они, вероятно, будут упакованы, но для объектов это нормально.

if (returnType == typeof(bool) { return (T)(object)GetAsBool(); }

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

static public explicit operator bool(MyClass source) => source.GetAsBool();
static public explicit operator string(MyClass source) => source.GetAsString();

Тогда люди, использующие ваш класс, смогут просто привести его к нужному типу, и если приведение не поддерживается, возникнет ошибка времени компиляции.

var x = (bool)myObject;
var y = (string)myObject;

Еще не знал о подходе с оператором преобразования, работает еще лучше. Спасибо!

johnny2 06.06.2024 12:09

Другие вопросы по теме

Похожие вопросы

Как решить проблему «ManagedIdentityCredential.GetToken не удалось получить токен доступа». при использовании Azure ServiceBusTrigger и управляемого удостоверения?
Селектор шаблонов данных нажимается только один раз, а не при обновлении полного свойства
Как разрешить окну использовать стиль по умолчанию, но не стиль инфраструктуры пользовательского интерфейса (Lepoco WPFUI) в WPF?
Можно ли сохранить только отношение и определенную таблицу в отношении «многие ко многим» с помощью Entity Framework Core?
ASP.NET Core — отключить HTTP/2 в HttpSys
Каковы текущие рекомендации по использованию API-интерфейсов async/await для замены событий .NET?
Azure SDK .NET — пакетное удаление больших двоичных объектов с помощью SAS
Конфигурация DBContext генерирует ошибку конфигурации
Можете ли вы безопасно использовать async await внутри обработчика событий async void в С#?
Необходимо реализовать AddKeyedSingleton с помощью функции Azure