Общий метод преобразования throw InvalidCastException
Я хочу реализовать тот же простой метод преобразования, но во время выполнения я получаю сообщение об ошибке.
Так что сценарий довольно прост. У меня есть тот же сервис, который возвращает мне список элементов типа External. У меня есть свой собственный класс WrapperExternal, который просто оборачивает этот класс и предоставляет ему некоторые дополнительные функции. У меня есть некоторый другой набор классов, которые наследуются от WrapExternal и добавляют различные функциональные возможности.
Я хочу создать универсальный метод, который принимает список элементов внешнего списка и возвращает список элементов указанного типа.
Код моего приложения:
static void Main(string[] args)
{
var items = GetItemsFromServer();
var converted = ConvertItems<SubWrapperExternal>(items).ToList();
}
public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
{
return externalItems
.Where( item => true)
.Select(item => (T)item);
}
Когда вы попытаетесь запустить этот код, вы получите исключение в строке (T):
An unhandled exception of type 'System.InvalidCastException' occurred in ConsoleApplication1.exe
Additional information:
Unable to cast object of type 'ConsoleApplication1.WrapperExternal' to type 'ConsoleApplication1.SubWrapperExternal'.
Вы знаете, как я могу сделать это, чтобы работать?
Код тестового приложения:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var items = GetItemsFromServer();
var converted = ConvertItems<SubWrapperExternal>(items).ToList();
}
private static List<External> GetItemsFromServer()
{
return new List<External>
{
new External{Name = "A"},
new External{Name = "B"},
new External{Name = "C"},
};
}
public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
{
return externalItems
.Where( item => true)
.Select(item => (T)item);
}
}
class External
{
public string Name { get; set; }
}
class WrapperExternal
{
public External External { get; private set; }
public WrapperExternal(External external)
{
External = external;
}
public static explicit operator WrapperExternal(External item)
{
return item != null ? new WrapperExternal(item) : null;
}
public static implicit operator External(WrapperExternal item)
{
return item != null ? item.External : null;
}
}
class SubWrapperExternal : WrapperExternal
{
public SubWrapperExternal(External external)
: base(external)
{
}
public static explicit operator SubWrapperExternal(External item)
{
return item != null ? new SubWrapperExternal(item) : null;
}
public static implicit operator External(SubWrapperExternal item)
{
return item != null ? item.External : null;
}
}
}
1 ответ
Операторы преобразования являются ошибкой для использования с обобщениями - обобщенные элементы не поддерживают никаких статических перегрузок операторов. Из-за этого (T)
cast выполняет проверку неконвертирующего типа (генерики должны использовать один и тот же IL для каждого возможного T
запомни) - базовый castclass
,
Единственный "простой" способ делать то, что вы хотите, это обманывать с dynamic
:
return externalItems.Select(item => (T)(dynamic)item);
Поскольку C#-специфичен dynamic
провайдер знает все общие правила C#, знает операторы преобразования и будет применять их по требованию. С этим связано небольшое снижение производительности, но оно не так плохо, как кажется на первый взгляд, поскольку стратегия кэшируется (как IL) один раз для каждого типа - она не выполняет отражение для каждого элемента.