【问题标题】:LINQ .Cast() extension method fails but (type)object worksLINQ .Cast() 扩展方法失败但(类型)对象有效
【发布时间】:2010-05-12 13:59:53
【问题描述】:

为了在某些 LINQ to SQL 对象和 DTO 之间进行转换,我们在 DTO 上创建了显式转换运算符。这样我们就可以做到以下几点:

DTOType MyDTO = (LinqToSQLType)MyLinq2SQLObj;

这很好用。

但是,当您尝试使用 LINQ .Cast() 扩展方法进行转换时,它会引发一个无效的转换异常,即无法将类型 Linq2SQLType 转换为类型 DTOType。即以下不起作用

List<DTO.Name> Names = dbContact.tNames.Cast<DTO.Name>()
                                               .ToList();

但以下工作正常:

DAL.tName MyDalName = new DAL.tName();
DTO.Name MyDTOName = (DTO.Name)MyDalName;

以下也可以正常工作

List<DTO.Name> Names = dbContact.tNames.Select(name => (DTO.Name)name)
                                               .ToList();

为什么 .Cast() 扩展方法会抛出一个无效的强制转换异常?我过去曾多次以这种方式使用 .Cast() 扩展方法,当您将基本类型之类的东西强制转换为派生类型时,它可以正常工作,但是当对象具有显式强制转换运算符时会失败。

【问题讨论】:

标签: c# linq casting extension-methods


【解决方案1】:

Cast&lt;&gt; 扩展方法不应用用户定义的转换。它只能转换为接口或在所提供类型的类层次结构内。

用户定义的转换是在编译时根据表达式中涉及的静态类型来识别的。它们不能用作运行时转换,因此以下是非法的:

public class SomeType
{
  public static implicit operator OtherType(SomeType s) 
  { 
    return new OtherType(); 
  }
}

public class OtherType { }

object x = new SomeType();
OtherType y = (OtherType)x; // will fail at runtime

SomeTypeOtherType 是否存在 UDC 无关紧要 - 它不能通过 object 类型的引用来应用。尝试运行上述代码会在运行时失败,报告如下:

System.InvalidCastException: 
    Unable to cast object of type 'SomeType' to type 'OtherType'

Cast&lt;&gt;() 只能执行表示保留转换...这就是为什么您不能使用它来应用用户定义的转换。

Eric Lippert 有一篇关于 behavior of the cast operator in C# 的精彩文章 - 总是值得一读。

【讨论】:

  • 我确实在我的问题中说,我已经观察到您通过经验描述的行为,我要问的是为什么它不适用于用户定义的转换。不使用显式转换运算符有什么不同,它是如何转换的?
  • Ben:在您撰写评论时,我正在努力详细说明我的答案 :) 如果这清楚地说明了 Cast&lt;&gt;() 的工作方式,请告诉我。
  • 好的,是的,这很有道理,谢谢,我用反射器做了一些挖掘,几乎无法辨认,但它确实暗示了你的解释,谢谢。
  • @Ben:想象一下,您正在构建一个类似 CLR 的运行时。您真的想在该运行时中嵌入 C# 编程语言的规则以进行绑定转换吗?如果 VB/F#/JScript/Perl/Ruby/C/C++/J#/Python/... 规则不同怎么办?为什么 C# 规则要在运行时得到特殊处理?因此,您在运行时获得的规则是最简单的“最小公分母”;只是一个类型测试。如果您想要编译时行为,请在 C# 4 中使用“动态”在运行时获取它,或者编写代码以便在编译时完成转换的解析。
【解决方案2】:

如果您反编译 Linq 程序集,您将获得类似于以下的代码。前面的答案是正确的,最终转换是从“对象”到目标类型,这对于自定义类型总是会失败。

private static IEnumerable<TResult> CastIterator<TResult>( IEnumerable source )
{
    foreach(object current in source)
    {
        yield return (TResult)( (object)current );
    }
    yield break;
}

public static IEnumerable<TResult> DCast<TResult>( this IEnumerable source )
{
    IEnumerable<TResult> enumerable = source as IEnumerable<TResult>;
    if(enumerable != null)
    {
        return enumerable;
    }
    if(source == null)
    {
        throw new ArgumentNullException( "source" );
    }
    return CastIterator<TResult>( source );
}

【讨论】:

  • 感谢您的意见,但我理解原来的答案。
【解决方案3】:

对于那些遇到此问题并寻找解决方法的人...

Dim res = arrayOfStrings.Select(Function(__) CType( __, YourType ))

不确定 C# 的确切语义,但我相信这很容易。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    相关资源
    最近更新 更多