ฉันมักจะต้องการที่จะคว้าองค์ประกอบแรกของIEnumerable<T>. สุทธิและฉันไม่ได้พบวิธีที่ดีที่จะทำ สิ่งที่ดีที่สุดที่ฉันคิดไว้คือ:
foreach(Elem e in enumerable) {
// do something with e
break;
}
yuck! ดังนั้นมีวิธีที่ดีในการทำเช่นนี้?
ฉันมักจะต้องการที่จะคว้าองค์ประกอบแรกของIEnumerable<T>. สุทธิและฉันไม่ได้พบวิธีที่ดีที่จะทำ สิ่งที่ดีที่สุดที่ฉันคิดไว้คือ:
foreach(Elem e in enumerable) {
// do something with e
break;
}
yuck! ดังนั้นมีวิธีที่ดีในการทำเช่นนี้?
คำตอบ:
หากคุณสามารถใช้ LINQ คุณสามารถใช้:
var e = enumerable.First();
สิ่งนี้จะทำให้เกิดข้อยกเว้นแม้ว่านับไม่ได้ว่าว่างเปล่า: ในกรณีนี้คุณสามารถใช้:
var e = enumerable.FirstOrDefault();
FirstOrDefault()จะกลับมาdefault(T)ถ้าจำนวนที่ว่างเปล่าซึ่งจะเป็นnullประเภทการอ้างอิงหรือเริ่มต้น 'zero-value' สำหรับประเภทค่า
หากคุณไม่สามารถใช้ LINQ ได้วิธีการของคุณจะถูกต้องทางเทคนิคและไม่แตกต่างจากการสร้างตัวแจงนับโดยใช้GetEnumeratorและMoveNextวิธีการเพื่อดึงผลลัพธ์แรก (ตัวอย่างนี้ถือว่าสมมติว่านับได้IEnumerable<Elem>):
Elem e = myDefault;
using (IEnumerator<Elem> enumer = enumerable.GetEnumerator()) {
if (enumer.MoveNext()) e = enumer.Current;
}
Joel Coehoornพูดถึง.Single()ในความคิดเห็น; สิ่งนี้จะใช้งานได้หากคุณคาดหวังว่าจำนวนของคุณจะมีองค์ประกอบหนึ่งอย่างแน่นอน - อย่างไรก็ตามมันจะส่งข้อยกเว้นหากองค์ประกอบนั้นว่างเปล่าหรือใหญ่กว่าองค์ประกอบหนึ่ง มีความสอดคล้องกันคือวิธีการที่ครอบคลุมสถานการณ์นี้ในลักษณะคล้ายกับSingleOrDefault() FirstOrDefault()อย่างไรก็ตามDavid Bอธิบายว่าSingleOrDefault()อาจยังมีข้อยกเว้นในกรณีที่ตัวนับมีมากกว่าหนึ่งรายการ
แก้ไข: ขอบคุณMarc Gravell ที่ชี้ให้เห็นว่าฉันต้องกำจัดIEnumeratorวัตถุของฉันหลังจากใช้มัน - ฉันได้แก้ไขตัวอย่างที่ไม่ใช่ LINQ เพื่อแสดงusingคำหลักเพื่อใช้รูปแบบนี้
ในกรณีที่คุณใช้. NET 2.0 และไม่มีสิทธิ์เข้าถึง LINQ:
static T First<T>(IEnumerable<T> items)
{
using(IEnumerator<T> iter = items.GetEnumerator())
{
iter.MoveNext();
return iter.Current;
}
}
สิ่งนี้ควรทำในสิ่งที่คุณกำลังมองหา ... มันใช้ generics เพื่อให้คุณได้ไอเท็มแรกใน IEnumerable ประเภทใดก็ได้
เรียกว่าเป็นเช่นนั้น:
List<string> items = new List<string>() { "A", "B", "C", "D", "E" };
string firstItem = First<string>(items);
หรือ
int[] items = new int[] { 1, 2, 3, 4, 5 };
int firstItem = First<int>(items);
คุณสามารถปรับเปลี่ยนได้อย่างง่ายดายเพียงพอที่จะเลียนแบบวิธีการขยาย IEnumerable.ElementAt () ของ NET 3.5:
static T ElementAt<T>(IEnumerable<T> items, int index)
{
using(IEnumerator<T> iter = items.GetEnumerator())
{
for (int i = 0; i <= index; i++, iter.MoveNext()) ;
return iter.Current;
}
}
เรียกมันว่าเป็นเช่นนั้น:
int[] items = { 1, 2, 3, 4, 5 };
int elemIdx = 3;
int item = ElementAt<int>(items, elemIdx);
แน่นอนถ้าคุณทำมีการเข้าถึง LINQ แล้วมีความอุดมสมบูรณ์ของคำตอบที่ดีเอาไว้แล้ว ...
คุณไม่ได้ระบุว่าจะใช้. Net เวอร์ชันใด
สมมติว่าคุณมี 3.5 วิธีอื่นคือวิธี ElementAt:
var e = enumerable.ElementAt(0);
เริ่มต้นหรือไม่
Elem e = enumerable.FirstOrDefault();
//do something with e
ลองนี้
IEnumberable<string> aa;
string a = (from t in aa where t.Equals("") select t.Value).ToArray()[0];
ใช้ FirstOrDefault หรือ foreach loop ดังที่ได้กล่าวไปแล้ว การดึงตัวแจงนับและการเรียกกระแสด้วยตนเองควรหลีกเลี่ยง foreach จะกำจัดตัวแจงนับให้คุณหากใช้ IDisposable เมื่อเรียกใช้ MoveNext และ Current คุณจะต้องทำการกำจัดด้วยตัวเอง (ถ้า aplicable)
หาก IEnumerable ของคุณไม่เปิดเผย<T>และ Linq ล้มเหลวคุณสามารถเขียนวิธีโดยใช้การสะท้อน:
public static T GetEnumeratedItem<T>(Object items, int index) where T : class
{
T item = null;
if (items != null)
{
System.Reflection.MethodInfo mi = items.GetType()
.GetMethod("GetEnumerator");
if (mi != null)
{
object o = mi.Invoke(items, null);
if (o != null)
{
System.Reflection.MethodInfo mn = o.GetType()
.GetMethod("MoveNext");
if (mn != null)
{
object next = mn.Invoke(o, null);
while (next != null && next.ToString() == "True")
{
if (index < 1)
{
System.Reflection.PropertyInfo pi = o
.GetType().GetProperty("Current");
if (pi != null) item = pi
.GetValue(o, null) as T;
break;
}
index--;
}
}
}
}
}
return item;
}
คุณสามารถลองเวอร์ชันทั่วไปที่ให้อิลิเมนต์ i
enumerable.ElementAtOrDefault (i));
หวังว่ามันจะช่วย