转换为自定义类型,Enumerable.Cast <t>和as关键字</t>

时间:2014-02-06 15:43:14

标签: c# linq casting ienumerable explicit-conversion

这更多是出于好奇而非必要性的问题,而且必须处理Active Directory(MS)类型,例如SearchResultCollection(在System.DirectoryServices命名空间中)。

经常在代码中处理AD时,我发现我必须检查null,Count,[0]的值...无论如何转换我得到的东西..一直希望底层的AD对象通过COM没有蠢事等。

在最近与Parallel.ForEach玩游戏之后 - 并且不得不传递IEnumerable<T>,我想,也许看看如何将SearchResultCollection投射到一个IEnumerable会很有趣我自己的自定义类型的SearchResult。在这种类型中,我将从DirectoryEntry对象中提取所有值,并将它们粘贴在我自己的.NET托管代码中。然后我会取消DirectorySearcherItem等等。

所以,已经知道为了向它提供带有它的源的Parallel.ForEach来做searchResultCollection.Cast()是一个好主意,我添加了一个显式运算符用于转换为我自己的类型(让我们称之为'{ {1}}“)。

我在ParallelForEachvar myItem = (Item)currentSearchResult

中对此进行了测试

美好的时光,我的演员操作员被调用,一切正常。然后我想,做searchResultCollection.Cast<Item>()之类的事情会很好。可悲的是,这没有奏效,没有击中演员的任何断点。

我做了一些谷歌搜索并发现了Jon Skeet回答的有用帖子:

IEnumerable.Cast<>

它的关键,使用.Select(...)来强制显式的强制转换操作。好的,但是,嗯。

我走了,也许是反汇编System.Core - &gt; System.Linq.Enumerable.Cast<TResult>,我注意到这个“演员”实际上正在做一个'as'关键字转换:

public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
    IEnumerable<TResult> enumerable = source as IEnumerable<TResult>;
    if (enumerable != null)
    {
        return enumerable;
    }
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    return CastIterator<TResult>(source);
}

我读了一些,发现了这个:

Implicit/Explicit conversion with respect to the "as" keyword

这里的最佳答案指出'as'不会调用任何转换运算符..使用(强制转换)。从语义上来说,我发现这有点奇怪,因为扩展方法被称为强制转换。它不应该是强制转换吗?毫无疑问,为什么不会发生这种情况,任何人都知道它是什么,这是一个非常好的理由?

4 个答案:

答案 0 :(得分:1)

  

从语义上讲,我发现这有点奇怪,因为扩展方法被称为强制转换。它不应该是强制转换吗?

如果需要,它会在CastIterator内投射每个元素 ...尽管使用了一个通用的强制转型,它不会使用你已定义的显式转换运算符。您应该将显式转换运算符视为顶部具有语法糖的自定义方法,而不是CLR在大多数情况下所关心的内容。

对于as运算符:只是用于说“如果这已经是正确类型的序列,我们就可以返回。”它在序列上用作整体,而不是每个元素。

在C#转换和CLR转换没有对齐的情况下,这实际上会导致一些稍微离奇的问题,尽管我不记得我第一次遇到的例子。

有关详情,请参阅Edulinq内的Cast/OfType post

答案 1 :(得分:1)

即使它使用了强制转换运算符而不是as,它仍然不会调用用户定义的显式运算符,所以它无关紧要。唯一的区别是抛出的异常类型。

运行时根本不知道显式运算符。根据CLR,无法将搜索结果转换为Item。当编译器注意到有一个与给定显式运算符匹配的强制转换时,它会在编译时将注入对该显式运算符(基本上是一个静态方法)的调用码。一旦你进入运行时就没有剩下的演员知识,只需要一个方法调用来处理转换。

因为这是显式运算符的实现方式,而不是向运行时提供如何进行转换的知识,Cast无法将显式运算符的调用注入代码中。它已经编译完毕。在编译时,没有任何明确的操作符注入,因此没有注入。

答案 2 :(得分:1)

是铸造。请参阅CastIterator的实现。

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

此处as关键字的使用仅用于检查整个集合是否可以投放到目标而不是逐项投射。如果as返回非空的内容,则返回整个集合,我们跳过整个迭代过程来投射每个项目。

例如,这个简单的例子将返回一个铸造集合,而不是迭代每个项目

int?[] collection = ...;
var castedCollection = collection.Cast<int?>()

因为as可以立即使用,所以无需迭代每个项目。

在此示例中,as提供null结果,我们必须使用CastIterator来检查每个对象

int?[] collection = ...;
var castedCollection = collection.Cast<object>()

答案 3 :(得分:1)

如果我理解正确,你需要一段我前面写过的DynamicCast

运行时不知道隐式和显式转换;这样做是编译器的工作。但是使用Enumerable.Cast<>你无法得到,因为Enumerable.Cast涉及从System.Object投射到Item,其中没有可用的转化(你有从X转换为Item,而不是ObjectItem

利用.Net4.0中的动态优势

public static class DynamicEnumerable
{
    public static IEnumerable<T> DynamicCast<T>(this IEnumerable source)
    {
        foreach (dynamic current in source)
        {
            yield return (T)(current);
        }
    }
}

将其用作

var result = collection.DynamicCast<Item>();