2

这是一个出于好奇而不是必要的问题,并且不得不处理诸如SearchResultCollection(在System.DirectoryServices命名空间中)之类的 Active Directory (MS) 类型。

经常在代码中处理 AD 时,我发现我必须检查 null、Count、[0] 的值 .. 并转换我得到的内容 .. 同时希望通过 COM 的底层 AD 对象不会不要去噗等。

Parallel.ForEach最近玩了 with 之后 - 并且不得不传入 an IEnumerable<T>,我想,看看我如何将 aSearchResultCollection转换IEnumerable为我自己的自定义类型可能会很有趣。在这种类型中,我会从SearchResult对象中提取所有值并将它们粘贴到我自己的 .NET 托管代码中。然后我会取消DirectoryEntry,DirectorySearcher等等等等。

因此,已经确定执行 searchResultCollection.Cast() 以便为 Parallel.ForEach 提供其源代码是一个好主意,我添加了一个显式运算符用于转换为我自己的类型(我们就称之为' Item')。

ParallelForEach我在,中对此进行了测试var myItem = (Item)currentSearchResult

好时光,我的演员操作员被调用,一切正常。然后我想,做类似的事情会很好searchResultCollection.Cast<Item>()。可悲的是,这不起作用,没有在演员阵容中遇到任何断点。

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

IEnumerable.Cast<>

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

我走了,也许是反汇编System.Core-> 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);
}

我阅读了更多内容并发现了这一点:

关于“as”关键字的隐式/显式转换

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

4

4 回答 4

1

如果我理解正确,您需要我前一段时间写的DynamicCast

运行时不知道隐式和显式转换;这是编译器的工作。但是使用Enumerable.Cast<>你无法得到它,因为Enumerable.Cast涉及从没有可用转换System.Object的地方转换(你有从 X 到 Item 的转换,而不是到)ItemObjectItem

在.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>();
于 2014-02-06T15:56:56.743 回答
1

语义上我觉得这有点奇怪,因为扩展方法被称为 cast.. 它不应该是强制转换吗?

如果需要,它会在...内转换每个元素CastIterator,尽管使用通用转换,但无论如何都不会使用您定义的显式转换运算符。您应该将显式转换运算符视为顶部带有语法糖的自定义方法,而不是 CLR 在大多数情况下关心的内容。

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

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

有关详细信息,请参阅Edulinq 中的Cast/OfType帖子。

于 2014-02-06T15:47:56.483 回答
1

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

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

因为这是显式运算符的实现方式,而不是向运行时提供如何进行转换的知识,所以无法Cast将显式运算符的调用注入代码中。它已经编译好了。编译时不知道要注入的任何显式运算符,因此没有注入。

于 2014-02-06T15:48:52.550 回答
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>()
于 2014-02-06T15:49:05.627 回答