Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用LINQ获取IEnumerable中的第一个(也是唯一一个)元素有什么好处吗<;T>;?_C#_Linq - Fatal编程技术网

C# 使用LINQ获取IEnumerable中的第一个(也是唯一一个)元素有什么好处吗<;T>;?

C# 使用LINQ获取IEnumerable中的第一个(也是唯一一个)元素有什么好处吗<;T>;?,c#,linq,C#,Linq,这两条线之间有什么显著的区别吗 var o = xmlFile.Descendants("SomeElement").ElementAt(0).Value; 以及: XmlFile是一个XDocument对象,并且子体(XName)返回IEnumerable 我知道First()将引发异常但在这种情况下这没关系;我已经针对XmlSchemaSet验证了我的XDocument对象,因此我知道元素存在。我假设如果集合为空,直接访问Value将抛出异常,因为ElementAt(0)也不会返回任何内容

这两条线之间有什么显著的区别吗

var o = xmlFile.Descendants("SomeElement").ElementAt(0).Value;
以及:

XmlFile
是一个
XDocument
对象,并且
子体(XName)
返回
IEnumerable

我知道
First()FirstOrDefault(),则code>将引发异常但在这种情况下这没关系;我已经针对
XmlSchemaSet
验证了我的
XDocument
对象,因此我知道元素存在。我假设如果集合为空,直接访问
Value
将抛出异常,因为
ElementAt(0)
也不会返回任何内容

但是是的;一、 显然,如果不需要,我不喜欢使用
指令添加
。在这种情况下,有没有理由使用LINQ?我无法想象在这两种情况下有任何真正的性能差异

我这样问是因为用户可以上传一个包含任意数量需要处理的XML文件的zip文件。每个XML文件1条“记录”

编辑:我最初的问题是“如何从IEnumerable中获取第一个元素而不使用System.Linq;
添加
,然后我在
中找到了
元素,没有意识到它们都是Linq的一部分

所以我想我真正想知道的是,上面的代码片段和下面的代码片段之间是否有区别:

var descendants = xmlFile.Descendants("SomeElement");
var enumerator = descendants.GetEnumerator();
var node = (enumerator.MoveNext()) ? enumerator.Current : null;

我肯定会说LINQ更具可读性,仅此一点就可能值得使用。但是,用户可以上传一个10 MB的zip文件,每个XML文件的大小从2 KB到10 KB不等,这取决于它是哪个模式。因此,这是一个相当多的文件。

检查源代码。这两个
元素都位于
First
是上定义的扩展方法(如问题注释中所述)

更新 我还包括了
Single
的实现,正如前面讨论的那样,对于这个特定的问题,它将是一个更好的选择。从根本上说,这归结为可读性和抛出的异常,因为它们都使用相同的方法访问第一个元素

    public static TSource First<TSource>(this IEnumerable<TSource> source) {
        if (source == null) throw Error.ArgumentNull("source");
        IList<TSource> list = source as IList<TSource>;
        if (list != null) {
            if (list.Count > 0) return list[0];
        }
        else {
            using (IEnumerator<TSource> e = source.GetEnumerator()) {
                if (e.MoveNext()) return e.Current;
            }
        }
        throw Error.NoElements();
    }


    public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index) {
        if (source == null) throw Error.ArgumentNull("source");
        IList<TSource> list = source as IList<TSource>;
        if(list != null) return list[index];
        if (index < 0) throw Error.ArgumentOutOfRange("index");
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            while (true) {
                if (!e.MoveNext()) throw Error.ArgumentOutOfRange("index");
                if (index == 0) return e.Current;
                index--;
            }
        }
    }

    public static TSource Single<TSource>(this IEnumerable<TSource> source) {
        if (source == null) throw Error.ArgumentNull("source");
        IList<TSource> list = source as IList<TSource>;
        if (list != null) {
            switch (list.Count) {
                case 0: throw Error.NoElements();
                case 1: return list[0];
            }
        }
        else {
            using (IEnumerator<TSource> e = source.GetEnumerator()) {
                if (!e.MoveNext()) throw Error.NoElements();
                TSource result = e.Current;
                if (!e.MoveNext()) return result;
            }
        }
        throw Error.MoreThanOneElement();
    }
publicstatictsource优先(此IEnumerable源){
if(source==null)抛出错误.ArgumentNull(“source”);
IList list=源作为IList;
如果(列表!=null){
如果(list.Count>0)返回列表[0];
}
否则{
使用(IEnumerator e=source.GetEnumerator()){
如果(e.MoveNext())返回e.Current;
}
}
抛出错误。NoElements();
}
公共静态TSource ElementAt(此IEnumerable源,int索引){
if(source==null)抛出错误.ArgumentNull(“source”);
IList list=源作为IList;
if(list!=null)返回列表[索引];
如果(索引<0)抛出错误。ArgumentOutOfRange(“索引”);
使用(IEnumerator e=source.GetEnumerator()){
while(true){
如果(!e.MoveNext())抛出错误.ArgumentOutOfRange(“索引”);
如果(索引==0)返回当前值;
索引--;
}
}
}
公共静态TSource Single(此IEnumerable源){
if(source==null)抛出错误.ArgumentNull(“source”);
IList list=源作为IList;
如果(列表!=null){
开关(list.Count){
案例0:抛出错误.NoElements();
案例1:返回列表[0];
}
}
否则{
使用(IEnumerator e=source.GetEnumerator()){
如果(!e.MoveNext())抛出错误.NoElements();
t源结果=e.电流;
if(!e.MoveNext())返回结果;
}
}
抛出错误。超过一个元素();
}

它们可以互换使用,因为它们都是在
System.Linq.Enumerable
中定义的。 但这里有一些细微的区别:

1) 如果没有返回结果,
.First
将抛出异常

2)
.ElementAt(0)
将在索引器超出范围时引发异常


使用
FirstOrDefault()
和/或
ElementAtOrDefault(0)可以避免这两种异常

唯一真正的区别是名称,但无论如何它都很重要。如果您只想让第一个项目可枚举。首先,如果您想让第一个项目,但可能以后还要让第二个、第三个项目等等。然后使用
ElementAt
/
ElementAtOrdefault

意图应该是不言自明的。可读性是这里的关键因素

您可以找到源代码,例如:


您可以看到,这两种方法都针对支持通过索引访问的集合进行了优化。

这里的其他答案指出,您提供的两个选项实际上都使用LINQ。但您更新的问题是,这是否等同于原始LINQ调用:

var descendants = xmlFile.Descendants("SomeElement");
var enumerator = descendants.GetEnumerator();
var node = (enumerator.MoveNext()) ? enumerator.Current : null;
嗯,不,不完全是。首先,请注意,
IEnumerator
实现了
IDisposable
,但是您的代码永远不会调用
Dispose
(尽管我怀疑在这种情况下这实际上会有任何影响)。其次,您的代码处理空数据集的方式不同于那些LINQ方法(您的实现更像是
FirstOrDefault
)。更等效的版本是:

XElement node;
using (var enumerator = xmlFile.Descendants("SomeElement").GetEnumerator()) 
{
    if (!enumerator.MoveNext()) 
    {
       throw new Exception(...); 
    }
    node = enumerator.Current;
}
或者不使用
,使用

XElement node;
var enumerator = xmlFile.Descendants("SomeElement").GetEnumerator();
try {
    if (!enumerator.MoveNext()) { throw new Exception(...); }
    node = enumerator.Current;
} finally {
    enumerator.Dispose();
}
但事实上,我们根本不需要
枚举器

var n = xmlFile.FirstNode;
var node = n as XElement;
while (node == null && n != null) 
{
    node = (n = n.NextNode) as XElement;
}
while (node != null &&  node.Name != "SomeElement") 
{
    node = (n = node.FirstNode ?? node.NextNode ?? node.Parent?.NextNode) as XElement;
    while (node == null && n != null) 
    {
        node = (n = n.NextNode) as XElement;
    }
}
if (node == null) 
{
    throw new Exception(""); 
}
现在,如果您分析这一点,您会发现更复杂的解决方案会带来一些边际性能提升。下面是我整理的一个相当基本的基准测试的结果(第一列没有编译器优化,第二列有编译器优化):

但是,可以节省一些处理器周期
var n = xmlFile.FirstNode;
var node = n as XElement;
while (node == null && n != null) 
{
    node = (n = n.NextNode) as XElement;
}
while (node != null &&  node.Name != "SomeElement") 
{
    node = (n = node.FirstNode ?? node.NextNode ?? node.Parent?.NextNode) as XElement;
    while (node == null && n != null) 
    {
        node = (n = n.NextNode) as XElement;
    }
}
if (node == null) 
{
    throw new Exception(""); 
}
Method       Mean (/o-)   Mean (/o+)
First()      0.1468333    0.1414340
ElementAt()  0.1452045    0.1419018
No Linq      0.1334992    0.1259622
While Loop   0.0895821    0.0693819
var node = xmlFile.Descendants("SomeElement").First();