C# 扩展方法使LINQ中断

C# 扩展方法使LINQ中断,c#,.net,linq,C#,.net,Linq,我想我遇到了扩展方法和LINQ的一个极端情况 今天,我声明了一些扩展方法,以使代码更具可读性。因此,我创建了一个扩展方法,用于获取对象并执行直接转换: 公共静态类泛型 { 公共静态T将此对象转换为o { 返回; } } 我们的目的是通过这样的方式调用我的direct Casts: 将两个扩展方法都放在不同的名称空间中而不引用,或者将强制转换重命名为不同的名称空间可以解决这个问题 我想进一步了解一下为什么会发生这种情况。它与LINQ有重叠吗?如果是这样,为什么我的演员有优先权 在.NETFidd

我想我遇到了扩展方法和LINQ的一个极端情况

今天,我声明了一些扩展方法,以使代码更具可读性。因此,我创建了一个扩展方法,用于获取对象并执行直接转换:

公共静态类泛型 { 公共静态T将此对象转换为o { 返回; } } 我们的目的是通过这样的方式调用我的direct Casts:

将两个扩展方法都放在不同的名称空间中而不引用,或者将强制转换重命名为不同的名称空间可以解决这个问题

我想进一步了解一下为什么会发生这种情况。它与LINQ有重叠吗?如果是这样,为什么我的演员有优先权

在.NETFiddle中查找代码

我想进一步了解一下为什么会发生这种情况。它与LINQ有重叠吗

对!

当您对表单进行查询时

from Foo bar in blah select baz
编译器会将其重写为一系列方法调用:

blah.Cast<Foo>().Select(bar => baz)
方法调用的解析与普通方法调用一样。如果blah有成员类型,则使用该类型;如果不是,则搜索扩展方法

如果是这样,为什么我的演员有优先权

解析扩展方法的规则有点棘手,但基本规则是包含类wins的最接近规则。如果您的扩展方法位于全局命名空间中的一个类中,而LINQ扩展方法位于System.LINQ命名空间中的一个类中,那么您的扩展就更接近。要访问类,编译器必须从当前名称空间上升到全局名称空间。要访问System.Linq,编译器必须先进入全局名称空间,然后再进入System.Linq以找到正确的类

特别要注意的是,C编译器不会回溯。当我尝试使用Select时,返回object的Cast版本给了我一个错误;还有其他版本的Cast有效吗?C编译器只是简单地说,Cast的最佳版本会给出一个错误,因此它不应该试图找到一个更糟糕的Cast版本。在这种情况下,这就是您想要的行为,但在许多情况下,您最终会得到一个意外的方法调用。C更喜欢给出一个错误,而不是试图猜测你真正想要的方法

这是对实际规则的过度简化,当多个嵌套名称空间中存在多个using指令时,实际规则会变得复杂。如果您需要确切的规则,请参阅规范

但最好先不要去那里。不要创建自己的名为Cast、Select、Where等扩展方法,除非您打算在其整体中复制LINQ功能

更新:我刚刚意识到我没有在这里解决一个潜在的更大的问题:您的cast方法可能不会执行您希望它执行的操作,而不管它的命名与查询模式的方法冲突这一事实

我注意到,您的cast方法在许多方面比仅使用cast操作符更糟糕:

Cast123不必要地将int装箱;int123没有。 Cast123失败,short123成功。不存在从装箱整型到短整型的转换。 假设您有一个从动物到形状的用户定义转换。铸造新老虎失败,塑造新老虎成功。 假设q是一个正好为null的可为null的int。卡斯特成功了!但是stringq在编译时会失败
等等。您的cast方法与实际的cast操作符有一些重叠,但它决不能替代它。如果您打算捕获cast运算符的语义,则需要使用dynamic,它在运行时再次启动编译器,并对运行时类型进行编译时分析。

您的cast方法隐藏了Linq cast方法。尝试重命名它。只需完全删除此方法,它是不必要的和危险的。实际上,它甚至比不使用任何字符的c语言使用更多的字符help@Chris为什么这很危险?就我个人而言,我认为可读性的好处是值得添加几个字符,因为这是一个演员安全还是直接演员危险还不清楚。一般来说,您不应该使用直接强制转换,它们是糟糕软件设计的一个非常明显的指标。直接强制转换几乎完全用于违反LSP。LSP将讨论实例的替换。这不是强制转换的直接问题,因为与您使用的强制转换类型相比,使用接口更能驱动转换。我将为直接强制转换的使用辩护,因为它是一种预期的类型,您不想用额外的逻辑来测试null来污染代码。”仅当您不确定类型并且希望继续工作流时,才应使用“as”强制转换。错误消息如何?它一点也不清楚,也没有给问题的根源提供任何有用的指示@disklosr:编写错误消息时假设使用查询语法的开发人员正在尝试创建查询,并且 实现查询模式的方法必须在范围内。这里的特定场景中,查询模式的一个方法在范围内,并隐藏了正确的方法,坦率地说,在我们设计错误消息时,我没有遇到这种场景。这是我们设计错误消息十年来我第一次想到它。
from Foo bar in blah select baz
blah.Cast<Foo>().Select(bar => baz)