为啥 Linq Cast<> 助手不能与隐式转换运算符一起使用?

Posted

技术标签:

【中文标题】为啥 Linq Cast<> 助手不能与隐式转换运算符一起使用?【英文标题】:Why does the Linq Cast<> helper not work with the implicit cast operator?为什么 Linq Cast<> 助手不能与隐式转换运算符一起使用? 【发布时间】:2013-01-09 12:26:51 【问题描述】:

在决定重复投票之前,请阅读到最后......

我有一个将implicit cast 运算符实现为另一种类型的类型:

class A

    private B b;
    public static implicit operator B(A a)  return a.b; 

class B


现在,隐式和显式转换都可以正常工作:

B b = a;
B b2 = (B)a;

...那么为什么 Linq 的 .Cast&lt;&gt; 没有呢?

A[] aa = new A[]...;
var bb = aa.Cast<B>();  //throws InvalidCastException

查看.Cast&lt;&gt; 的源代码,并没有什么神奇之处:如果参数确实是IEnumerable&lt;B&gt;,则有一些特殊情况,然后:

foreach (object obj in source) 
    yield return (T)obj; 
    //            ^^ this looks quite similar to the above B b2 = (B)a;

那么为什么 my 显式转换有效,而 .Cast&lt;&gt; 内部的转换无效?

编译器是否完善了我的显式转换?

PS。我看到了this question,但我认为它的答案并不能真正解释发生了什么。

【问题讨论】:

即使“它的答案并不能真正解释发生了什么”你也不应该问重复的问题;) @Tim 除了提出更好的问题外,您建议我如何改进某个主题的答案(我不知道答案)? @TimSchmelter,如果这个问题得到更好的答案,也许另一个应该作为这个副本关闭? 我不知道处理这个问题的正确方法是什么。我经常看到问题的答案比建议的副本更好。 @tim 显然关于 meta 的共识是合并问题:meta.stackexchange.com/q/1375/136203 - 我已经标记了这个问题,让我们看看会发生什么:) 【参考方案1】:

那么为什么我的显式演员表起作用,而 .Cast 里面的演员表不起作用?

您的显式转换知道在编译时源和目标类型是什么。编译器可以发现显式转换,并发出代码来调用它。

不是泛型类型的情况。请注意,这并不特定于 Cast 或一般的 LINQ - 如果您尝试简单的 Convert 方法,您会看到同样的事情:

public static TTarget Convert<TSource, TTarget>(TSource value)

    return (TTarget) value;

不会调用任何用户定义的转换 - 甚至是从(例如)intlong 的转换。它将执行参考转换和装箱/拆箱转换。这只是泛型工作方式的一部分。

【讨论】:

那么是否应该始终定义显式运算符来补充隐式运算符?隐式运算符不“隐式”包含显式转换是否有原因? 有没有办法让它工作?在某些情况下它会非常有用——比如在使用甚至没有实现 IConvertible... 的 Oracles 愚蠢类型时 @DanielHilgarth:我猜你最接近的是使用dynamic @WillVousden:可以显式调用隐式运算符。在上面的示例代码中添加显式运算符不会改变观察到的行为。 @WillVousden:这与转换是显式还是隐式无关 - 只是,因为您的转换是用户定义的。通用转换操作根本不会使用它。【参考方案2】:

简短的回答很简单:Cast&lt;T&gt; 方法不支持自定义转换运算符。

在第一个例子中:

B b = a;
B b2 = (B)a;

编译器可以在静态分析期间看到这个B(A a) 运算符;编译器将其解释为自定义运算符方法的静态 call。在第二个例子中:

foreach (object obj in source) 
    yield return (T)obj; 

对操作员一无所知;这是通过 unbox.any 实现的(如果 T 是 ref 类型,则与 castclass 相同)。

还有第三种选择:如果你通过dynamic,运行时实现会尝试模仿编译器规则,所以这个找到操作符...但不是作为C#的一部分-to-IL 编译步骤:

dynamic b = a; // note that `dynamic` here is *almost* the same as `object`
B b2 = b;

【讨论】:

你是对的。我试过这个,它抛出:B b = (B)(object)a; 我认为这是因为我在静态上下文中隐藏了a 的真实类型...... @CristiDiaconescu 是的;通过object,它变成了一个简单的castclass,而castclass不支持自定义操作符 顺便说一句。我认为在你的最后一个例子中你的意思是B b2 = (B) b ; 除此之外,您可以通过继承树进行强制转换,因此最终一切都可以强制转换为object。因此(new DerivedType[] ... ).Cast&lt;ParentType&gt;() 始终有效,(new ParentType[] new DerivedType(...), ... )).Cast&lt;DerivedType&gt;() 在所有元素都是派生类型时有效。【参考方案3】:

Enumerable.Cast&lt;T&gt; 是一种 .Net 框架方法,其行为适用于所有调用它的 .Net 语言。

另请参阅 Ander Hejlsberg 对 this discussion 的回复。


编译器是否完善了我的显式转换?

所谓的“隐式转换运算符”实际上是“implicit conversion operator”。这是一个常见的错误。

C# 允许您使用强制转换语法指定转换。发生这种情况时,您使用的是不同的实例(转换),而不是更改对同一实例的引用(转换)。

【讨论】:

以上是关于为啥 Linq Cast<> 助手不能与隐式转换运算符一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

何时在 Linq 中使用 Cast() 和 Oftype()

为啥我不能对 Visio Masters 集合使用 Linq 查询?

Linq 多连接及 left join 实例 记录

为啥我不能在多重继承期间动态转换“sideways”?

为啥使用 static_cast<int>(x) 而不是 (int)x?

为啥我看到 .Cast<int>() 和 .Select(a => (int)a) 之间存在差异?