将Casting List<x>转换为List<y>

11

以下代码有效:

List<JsonStock> stock = new List<JsonStock>();

foreach(tblStock item in repository.Single(id).tblStocks)                
    stock.Add((JsonStock) item);

因此,您可能认为这段代码也可以工作:

List<JsonStock> stock = repository.Single(id).tblStocks.Cast<JsonStock>().ToList()

但是我遇到了错误无效的转换操作,有人知道为什么会出现这种情况吗?

更新

tblStocks是一个LINQ to SQL对象列表,它的类名叫做tblStock。
JsonStock是tblStock类的简化版本,并作为JSON对象返回给网页。

下面的操作符是用来进行类型转换的:

public partial class tblStock{
    public static explicit operator JsonStock(tblStock stock){
        JsonStock item = new JsonStock
        {
            boxes = stock.boxes,
            boxtype = stock.tblBoxType.name,
            boxtype_id = stock.boxtype_id,
            grade = stock.grade,
            packrate = stock.packrate,
            weight = stock.weight
        };

        return item;
    }
}

3
tblStocks 的类型是如何定义的? - Daniel A. White
@Marco - 不应该有任何抱怨。 - Daniel A. White
使用调试器尝试 foreach(tblStock item in repository.Single(id).tblStocks.Cast(JsonStock)) 并观察它在哪里崩溃(通过计算插入到 List<> stock 中的数量) - xanatos
3
tblStock和JsonStock之间的关系是什么? - Strillo
2
假设tblStocks是JsonStock的IEnumerable,你的代码应该可以工作。那么“在tblStocks中是什么”?我假设那里也有继承“JsonStock”类的对象,因为那会导致转换异常。 - user604613
显示剩余6条评论
5个回答

5

Cast 用于将非泛型集合转换为泛型集合,即执行拆箱操作。它不能按您想要的方式使用。
当您查看 Cast 和它使用的 CastIterator 的实现时,您会发现它接受一个对象并将其强制转换为指定的类型:

foreach (object current in source)
{
    yield return (TResult)current;
}

只有当current确实是TResult时,才能起作用。在这种情况下不应用自定义转换。
这是默认行为,您可以自行测试:

double d = 0.0;
object tmp = d;
int i = (int)tmp; // throws the same exception you are getting

如果 tblStocks 是一个通用的可枚举对象,那么您最好使用简单的 Select 实现您所需的功能。
List<JsonStock> stock = repository.Single(id).tblStocks
                                  .Select(x => (JsonStock)x).ToList();

或者,如果tblStocks是一个非泛型的可枚举对象,你需要结合使用CastSelect

List<JsonStock> stock = repository.Single(id).tblStocks.Cast<tblStock>()
                                  .Select(x => (JsonStock)x).ToList();

这将首先将tblStocks对象解包为它们的真实类型(tblStock),然后将其转换为您所需的类型(JsonStocks)。


2
隐式和显式转换运算符被Cast忽略。在你的情况下,这意味着:
 public static explicit operator JsonStock(tblStock stock)

被 Cast 忽略了,但在 foreach 的情况下不会被忽略。

谁说的?如果那是真的,Enumerable.Cast<T>()就毫无意义了。http://msdn.microsoft.com/en-us/library/bb341406.aspx没有任何信息支持你的说法。 - VVS
@VVS:你不理解 Enumerable.Cast<T>() 的目的。输入是一个非泛型可枚举对象,结果是一个泛型可枚举对象。这个扩展方法的整个目的是从你已知对象类型的非泛型可枚举对象中创建强类型可枚举对象。请参阅我的答案以获取更多详细信息。 - Daniel Hilgarth
@VVS 如果仔细阅读该链接,确实包含了支持我的说法的信息,我的回答中也有一个链接提供了相关信息。 你提供的链接谈到的是_cast_操作符,而上面的是_conversion_操作符。两者使用的语法相同(SomeType),但这并不意味着它们是相同的。所以我真的不明白你为什么要给我点踩。 - Rune FS

1

考虑使用OfType而不是Cast。在Cast中,如果您处理的项不是所需类型,则会出现InvalidCastException。使用OfType,它会捕获无效的转换,并且仅返回实际上为您寻找的类型的项目。

List<JsonStock> stock = repository.Single(id).tblStocks.OfType<JsonStock>().ToList() 

如果您返回空列表,我会怀疑您的 tblStocks 实际上并没有返回 JsonStocks,而是尝试将某些其他类型(tblStock?)投影到 DTO(JsonStock)中。如果后者是情况,则需要使用 Select 从底层类型投影到新类型中。
List<JsonStock> stock = repository.Single(id).tblStocks
                        .Select(stock => new JsonStock 
                             { 
                               Id = stock.Id,
                               Val1 = stock.Val1,
                               Val2 = stock.Val2,
                               ...
                             }
                         .ToList();

1

啊,显式运算符重载的奇妙之处。

所以为了解决你的问题,你可能需要先调用一个 Select。

List<JsonStock> stock = repository.Single(id).tblStocks.Select(x => (JsonStock)x).ToList() 

然而,我认为你应该摆脱这个运算符重载。考虑用类似于复制构造函数的实现来替换它。

class JsonStock
{
     public JsonStock(tblStock other)
     {
          // copy values here
     }
}

1
  • tblStocks.Cast<JsonStock>() 执行强制转换。
  • (JsonStock) item 执行强制转换或应用自定义定义的转换

由于 tblStock 是一个 LINQ to SQL 类,而 JsonStock 是您创建的自定义类,因此它们之间没有子类型。因此,您不能在两者之间进行强制转换。

要解决这个问题,您可以使用 LINQ 的 Select 子句并手动转换元素:

 List<JsonStock> stock = repository.Single(id).tblStocks
     .Select(item => (JsonStock) item).ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接