.Net içindeki bir IEnumerable <T> öğesinden ilk öğeyi nasıl alabilirim?


157

Sık sık bir IEnumerable<T>.net içindeki ilk öğeyi almak istiyorum ve bunu yapmak için iyi bir yol bulamadım. Geldiğim en iyi şey:

foreach(Elem e in enumerable) {
  // do something with e
  break;
}

Yuck! Bunu yapmanın güzel bir yolu var mı?

Yanıtlar:


242

LINQ kullanabiliyorsanız şunları kullanabilirsiniz:

var e = enumerable.First();

Numaralandırılabilir boşsa bu bir istisna atar: bu durumda şunları kullanabilirsiniz:

var e = enumerable.FirstOrDefault();

FirstOrDefault()döner default(T)numaralandırılabilir boşsa olacak, nullbaşvuru türleri ya da değer türleri için varsayılan 'sıfır değeri' için.

LINQ kullanamıyorsanız, yaklaşımınız teknik olarak doğrudur ve ilk sonucu almak için GetEnumeratorve MoveNextyöntemlerini kullanarak bir numaralandırıcı oluşturmaktan farklı değildir (bu örnek numaralandırılabilir bir değerdir IEnumerable<Elem>):

Elem e = myDefault;
using (IEnumerator<Elem> enumer = enumerable.GetEnumerator()) {
    if (enumer.MoveNext()) e = enumer.Current;
}

Joel Coehoorn söz .Single()yorumlarda; numaralandırılacak öğenizin tam olarak bir öğe içermesini bekliyorsanız bu da işe yarayacaktır - ancak boş veya bir öğeden daha büyükse bir istisna atar. SingleOrDefault()Bu senaryoyu benzer şekilde kapsayan karşılık gelen bir yöntem vardır FirstOrDefault(). Ancak, David B şöyle açıklıyor:SingleOrDefault() , sayılabilirin birden fazla öğe içerdiği durumlarda hala bir istisna .

Düzenleme: Marc Gravell , IEnumeratorkullandıktan sonra nesnemi atmam gerektiğini işaret ettiğin için teşekkürler - usingBu kalıbı uygulamak için anahtar kelimeyi görüntülemek için LINQ olmayan örneği düzenledim .


2
SingleOrDefault'un geri döneceğini belirtmeye değer 1) Sadece bir öğe varsa tek öğe. 2) hiçbir öğe varsa null. 3) birden fazla öğe varsa bir istisna atın.
Amy B

İyi arama David B - bir not ekledi.
Erik Forbes

36

.NET 2.0 kullanıyorsanız ve LINQ'ya erişiminiz yoksa:

 static T First<T>(IEnumerable<T> items)
 {
     using(IEnumerator<T> iter = items.GetEnumerator())
     {
         iter.MoveNext();
         return iter.Current;
     }
 }

Bu, aradığınızı yapmalıdır ... jenerik ilaç kullanır, böylece her tür IEnumerable'da ilk öğeyi alırsınız.

Öyle söyle:

List<string> items = new List<string>() { "A", "B", "C", "D", "E" };
string firstItem = First<string>(items);

Veya

int[] items = new int[] { 1, 2, 3, 4, 5 };
int firstItem = First<int>(items);

.NET 3.5'in IEnumerable.ElementAt () uzantısı yöntemini taklit edecek kadar kolayca değiştirebilirsiniz:

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;
    }
} 

Böyle çağırmak:

int[] items = { 1, 2, 3, 4, 5 };
int elemIdx = 3;
int item = ElementAt<int>(items, elemIdx);

Eğer Tabii do LINQ erişebilir, sonra iyi cevaplar bol zaten yayınlanmıştır var ...


Hata! Tabii ki haklısın, teşekkürler. Örneklerimi düzelttim.
BenAlabaster

2.0 kullanıyorsanız, var da yok.
özyinelemeli

1
Peki sanırım titiz almak istiyorsan onları düzgün ilan edeceğim: P
BenAlabaster

.NET 2.0 kullanarak sıkışmış biri olarak, bu büyük takdir! Kudos.
kayleeFrye_onDeck

26

Hangi .Net sürümünü kullandığınızı belirtmediniz.

3.5'e sahip olduğunuzu varsayarsak, başka bir yol ElementAt yöntemidir:

var e = enumerable.ElementAt(0);

2
ElementAt (0), hoş ve basit.
JMD


1

bunu dene

IEnumberable<string> aa;
string a = (from t in aa where t.Equals("") select t.Value).ToArray()[0];

0

Daha önce de belirtildiği gibi FirstOrDefault veya bir foreach döngüsü kullanın. Bir numaralandırıcıyı manuel olarak getirmek ve Akım'ı çağırmaktan kaçınılmalıdır. foreach, IDisposable uygularsa, numaralandırıcıyı sizin için atacaktır. MoveNext ve Current çağrılarını manuel olarak atmanız gerekir (eğer uygulanabilirse).


1
Sayıcıdan kaçınılması gereken kanıtlar nelerdir? Makinemdeki performans testleri, foreach'a göre yaklaşık% 10'luk bir performans kazancı olduğunu gösteriyor.
BenAlabaster

Neyi saydığınıza bağlıdır. Bir Erumerator, Veritabanına bağlantıyı kapatabilir, dosya tutamacını kapatabilir ve kapatabilir, bazı kilitli nesneleri serbest bırakabilir, vb.
Mouk

0

IEnumerable'ınız bunu göstermezse <T>ve Linq başarısız olursa, yansıma kullanarak bir yöntem yazabilirsiniz:

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;
}

0

ayrıca size ith öğesini veren daha genel sürümü de deneyebilirsiniz

enumerable.ElementAtOrDefault (i));

Umarım yardımcı olur

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.