为啥 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<>
没有呢?
A[] aa = new A[]...;
var bb = aa.Cast<B>(); //throws InvalidCastException
查看.Cast<>
的源代码,并没有什么神奇之处:如果参数确实是IEnumerable<B>
,则有一些特殊情况,然后:
foreach (object obj in source)
yield return (T)obj;
// ^^ this looks quite similar to the above B b2 = (B)a;
那么为什么 my 显式转换有效,而 .Cast<>
内部的转换无效?
编译器是否完善了我的显式转换?
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;
这不会调用任何用户定义的转换 - 甚至是从(例如)int
到 long
的转换。它将仅执行参考转换和装箱/拆箱转换。这只是泛型工作方式的一部分。
【讨论】:
那么是否应该始终定义显式运算符来补充隐式运算符?隐式运算符不“隐式”包含显式转换是否有原因? 有没有办法让它工作?在某些情况下它会非常有用——比如在使用甚至没有实现IConvertible
... 的 Oracles 愚蠢类型时
@DanielHilgarth:我猜你最接近的是使用dynamic
。
@WillVousden:可以显式调用隐式运算符。在上面的示例代码中添加显式运算符不会改变观察到的行为。
@WillVousden:这与转换是显式还是隐式无关 - 只是,因为您的转换是用户定义的。通用转换操作根本不会使用它。【参考方案2】:
简短的回答很简单:Cast<T>
方法不支持自定义转换运算符。
在第一个例子中:
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<ParentType>()
始终有效,(new ParentType[] new DerivedType(...), ... )).Cast<DerivedType>()
在所有元素都是派生类型时有效。【参考方案3】:
Enumerable.Cast<T>
是一种 .Net 框架方法,其行为适用于所有调用它的 .Net 语言。
另请参阅 Ander Hejlsberg 对 this discussion 的回复。
编译器是否完善了我的显式转换?
所谓的“隐式转换运算符”实际上是“implicit conversion operator”。这是一个常见的错误。
C# 允许您使用强制转换语法指定转换。发生这种情况时,您使用的是不同的实例(转换),而不是更改对同一实例的引用(转换)。
【讨论】:
以上是关于为啥 Linq Cast<> 助手不能与隐式转换运算符一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
何时在 Linq 中使用 Cast() 和 Oftype()
为啥我不能对 Visio Masters 集合使用 Linq 查询?