У меня есть набор методов, которые просто возвращают сохраненный «объект» как каждый из разных типов, например:
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 больше не привел к появлению каких-либо идей с моей стороны. Возможно ли то, что я пытаюсь сделать?
Какая польза от этого по сравнению с просто разоблачением GetAsString
и т. д.? Но если вы действительно этого хотите, проверьте dotnetfiddle.net/MpXwvz .
Ошибка «Невозможно преобразовать тип «bool» в «T» связана с тем, что вы смешиваете типы значений и ссылочные типы в одном универсальном методе. Одним из обходных путей является применение преобразования упаковки ко всем типам значений путем выполнения промежуточного приведения к Object
, но это неэффективно, поскольку приводит к выделению кучи.
Пожалуйста, не делитесь кодом с таким количеством очевидных опечаток.
Вы неправильно понимаете, как работают дженерики. Чтобы вызвать универсальный метод, вам все равно необходимо знать универсальный тип при написании кода для вызова метода. Он не откладывается до времени выполнения. Вам все равно придется написать что-то вроде var str = Get<string>();
, так что в этом нет никакого преимущества перед var str = GetAsString();
. Смысл обобщений состоит в том, чтобы дать вам возможность написать код один раз и заставить его работать для нескольких разных типов, а не откладывать проверку типов до времени выполнения.
@Dai Это не имеет ничего общего с боксом. Попытка привести ссылочный тип к T
без какого-либо общего ограничения приведет к той же проблеме. Это потому, что компилятор не может гарантировать безопасное приведение к T
.
@jmcilhinney Может быть полезно, если Get<T>
часто вызывается в других универсальных методах. Это предотвратит проведение этих проверок типов в нескольких местах.
Чтобы получить то, что вы просите, вы можете выполнить двойное приведение сначала к 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;
Еще не знал о подходе с оператором преобразования, работает еще лучше. Спасибо!
return default
здесь не следует использовать (поскольку он не позволяет потребителям обнаружить ошибочные состояния). Вместо этого вам следует создать исключение или использовать метод в стилеBoolean TryGet( out T value )
.