通用转换方法抛出InvalidCastException [英] Generic conversion method 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的<外部> externalItems)其中,T:WrapperExternal
{
返回externalItems
。凡(项目=>真)
。选择(项目=> (T)项);
$ / code>
当您尝试运行此代码时,您会在行(T) item:
在ConsoleApplication1.exe中出现未处理的异常类型'System.InvalidCastException'
其他信息:
无法投射ConsoleApplication1.WrapperExternal类型的对象来键入ConsoleApplication1.SubWrapperExternal。
你知道我该怎么做才行?
测试应用程序代码:
命名空间ConsoleApplication1
{
类程序
{
static void Main(string [] args)
{
var items = GetItemsFromServer();
var converted = ConvertItems< SubWrapperExternal>(items).ToList();
}
private static List< External> GetItemsFromServer()
{
返回新列表< External>
{
新外部{名称=A},
新外部{名称=B},
新外部{名称=C},
};
}
public static IEnumerable< T> ConvertItems< T>(IEnumerable的<外部> externalItems)其中,T:WrapperExternal
{
返回externalItems
。凡(项目=>真)
。选择(项目=> (T)项);
}
}
class External
{
public string Name {get;组; }
}
class WrapperExternal
{
public External External {get;私人设置; }
public WrapperExternal(外部外部)
{
External = external;
}
public static显式运算符WrapperExternal(External item)
{
return item!= null?新的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?新的SubWrapperExternal(item):null;
}
public static implicit operator External(SubWrapperExternal item)
{
return item!= null? item.External:null;
转换运算符与泛型一起使用时很简单 - 泛型不支持任何静态运算符重载。因此,(T)
cast正在执行非转换类型检查(泛型需要对每个可能的 T
,请记住) - 一个基本的
castclass
。
唯一的简单你想要欺骗动态
:
return externalItems.Select( item =>(T)(dynamic)item);
由于C#特定的动态
提供程序知道所有C#的常见规则,它知道转换操作符,并将按需应用它们。与此相关的性能成本略有降低,但并不像它乍看起来那么糟糕,因为该策略被缓存(如IL)每种类型一次 - 它不执行每项反射。
I want to implement same simple generic conversion method but on runtime I am getting an error.
So the scenario is quite simple. I have same service that return me list of items of type External. I have my own WrapperExternal class that simply wrap this class and expose some additional functionality to it. I have some another set of classes that inherit from WrapExternal and add different functionalities.
I want to create generic method that accept list of External list items and return list of items of specified type.
My application code:
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);
}
When you try to run this code you will get exception in line (T)item:
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'.
Do you know how can I make it to works?
Test application code:
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;
}
}
}
Conversion operators are a faff to use with generics - generics don't support any static operator overloads. Because of this, the (T)
cast is performing a non-converting type check (generics need to use the same IL for every possible T
, remember) - a basic castclass
.
The only "simple" way of doing what you want is to cheat with dynamic
:
return externalItems.Select(item => (T)(dynamic)item);
Since the C#-specific dynamic
provider knows all the common rules of C#, it knows about conversion operators, and will apply them on-demand. There is a slight performance cost associated with this, but it isn't as bad as it looks at first glance, as the strategy is cached (as IL) once per type - it doesn't perform per-item reflection.
这篇关于通用转换方法抛出InvalidCastException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!