IList'i C # 'da sıralama


87

Bu yüzden bugün ilginç bir sorunla karşılaştım. IList döndüren bir WCF web hizmetimiz var. Ben sıralamak isteyene kadar o kadar da önemli değil.

IList arayüzünün yerleşik bir sıralama yöntemine sahip olmadığı ortaya çıktı.

ArrayList.Adapter(list).Sort(new MyComparer())Sonunda sorunu çözmek için bu yöntemi kullandım ama bana biraz "getto" gibi geldi.

Bir uzantı yöntemi yazmakla oynadım, ayrıca IList'ten miras alıp kendi Sort () yöntemimi uygulayarak ve bir Listeye yayınlama ile oynadım, ancak bunların hiçbiri aşırı derecede zarif görünmüyordu.

Öyleyse sorum şu, bir IList'i sıralamak için zarif bir çözümü olan


Neden en başta bir IList'i geri verdiniz? Bir WCF hizmetinden mi?
DaeMoohn

Yanıtlar:


55

LINQ To Objects'i sizin için sıralamak için kullanmaya ne dersiniz?

Biriniz olduğunu IList<Car>ve arabanın bir Enginemülkü olduğunu varsayalım, aşağıdaki şekilde sıralayabileceğinizi düşünüyorum:

from c in list
orderby c.Engine
select c;

Düzenleme: Burada yanıt almak için hızlı olmanız gerekir. Diğer cevaplardan biraz farklı bir sözdizimi sunduğumda, cevabımı bırakacağım - ancak sunulan diğer cevaplar da aynı derecede geçerlidir.


3
Bazı senaryolarda arzu edilmeyen yeni bir numaralandırma yaratacaktır. Bildiğim kadarıyla ArrayList.Adapter yönteminin kullanılması dışında, arabirim aracılığıyla bir IList <T> yerinde sıralayamazsınız.
Tanveer Badar

69

LINQ kullanabilirsiniz:

using System.Linq;

IList<Foo> list = new List<Foo>();
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar);
IList<Foo> sortedList = sortedEnum.ToList();

61

Bu soru bir blog yazısı yazmam için bana ilham verdi: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

İdeal olarak, .NET Framework'ün bir IList <T> kabul eden statik bir sıralama yöntemi içereceğini düşünüyorum, ancak bir sonraki en iyi şey kendi uzantı yönteminizi oluşturmaktır. Bir IList <T> 'yi bir List <T> gibi sıralamanıza izin verecek birkaç yöntem oluşturmak çok zor değil. Bonus olarak, LINQ OrderBy uzantı yöntemini aynı tekniği kullanarak aşırı yükleyebilirsiniz, böylece List.Sort, IList.Sort veya IEnumerable.OrderBy kullanıyor olun, tam olarak aynı sözdizimini kullanabilirsiniz.

public static class SortExtensions
{
    //  Sorts an IList<T> in place.
    public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
    {
        ArrayList.Adapter((IList)list).Sort(new ComparisonComparer<T>(comparison));
    }

    // Sorts in IList<T> in place, when T is IComparable<T>
    public static void Sort<T>(this IList<T> list) where T: IComparable<T>
    {
        Comparison<T> comparison = (l, r) => l.CompareTo(r);
        Sort(list, comparison);

    }

    // Convenience method on IEnumerable<T> to allow passing of a
    // Comparison<T> delegate to the OrderBy method.
    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison)
    {
        return list.OrderBy(t => t, new ComparisonComparer<T>(comparison));
    }
}

// Wraps a generic Comparison<T> delegate in an IComparer to make it easy
// to use a lambda expression for methods that take an IComparer or IComparer<T>
public class ComparisonComparer<T> : IComparer<T>, IComparer
{
    private readonly Comparison<T> _comparison;

    public ComparisonComparer(Comparison<T> comparison)
    {
        _comparison = comparison;
    }

    public int Compare(T x, T y)
    {
        return _comparison(x, y);
    }

    public int Compare(object o1, object o2)
    {
        return _comparison((T)o1, (T)o2);
    }
}

Bu uzantılarla, IList'inizi bir Listede yaptığınız gibi sıralayın:

IList<string> iList = new []
{
    "Carlton", "Alison", "Bob", "Eric", "David"
};

// Use the custom extensions:

// Sort in-place, by string length
iList.Sort((s1, s2) => s1.Length.CompareTo(s2.Length));

// Or use OrderBy()
IEnumerable<string> ordered = iList.OrderBy((s1, s2) => s1.Length.CompareTo(s2.Length));

Gönderide daha fazla bilgi var: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/


1
Doğru yaklaşım, gerçekten bir ISortableList<T>arayüz sunmak (belirli bir karşılaştırıcı kullanarak listenin bir bölümünü sıralama yöntemleriyle), onu uygulamak ve uygulanıp uygulanmadığını kontrol ederek List<T>herhangi birini sıralayabilen statik bir yönteme sahip olmak olurdu. bir diziye kopyalamak, sıralamak, temizlemek ve öğeleri yeniden eklemek. IList<T>ISortableList<T>IList<T>
supercat

4
Harika cevap! Bununla birlikte, bir uyarı: bu yaklaşım IList<T> list, jenerik olmayan IListarayüze dönüştürülebileceğini varsayar . IList<T>Arabirimi uygulayarak kendi sınıfınızı kodlarsanız, genel olmayan IListarabirimi de uyguladığınızdan emin olun , aksi takdirde kod bir sınıf dönüştürme istisnası ile başarısız olur.
sstan

1
@supercat: ISortableList<T>Henüz mevcut olmayan ne teklif olabilir IList<T>? Veya farklı bir IList<T>şekilde sorulduğunda , öğeleri hayal ettiğiniz statik yöntemle yeniden eklemeden neden yerinde sıralanamıyor?
VEYA Mapper

@ORMapper: Bir liste, destek deposu olarak bir dizi kullanıyorsa (yaygın, ancak gerekli değil), dizi öğelerine doğrudan erişen bir sıralama rutini, IList<T>her öğeye erişmek için arabirimden geçmesi gerekenden çok daha hızlı olabilir . Hız farkı yeterince büyüktür ki, çoğu durumda bir listeyi bir diziye kopyalamak, diziyi sıralamak ve listeyi geri kopyalamak, listeyi yerinde bir sıralama rutini işlemesi yapmaya çalışmaktan daha hızlı olabilir.
supercat

1
ComparisonComparerSınıf gerekli değildir. Bunun Comparer<T>.Create(comparison)yerine standart statik yöntemi kullanabilirsiniz .
linepogl

9

Sanırım bunun gibi bir şey yapmanız gerekecek (daha somut bir türe dönüştürün).

Belki bunu ArrayList yerine T Listesine alabilirsin, böylece tip güvenliği ve karşılaştırıcıyı nasıl uygulayacağın için daha fazla seçenek elde edebilirsin.


4

@DavidMills tarafından kabul edilen cevap oldukça iyi, ancak daha iyi hale getirilebileceğini düşünüyorum. Birincisi, ComparisonComparer<T>çerçeve zaten statik bir yöntem içerdiğinde sınıfı tanımlamaya gerek yoktur Comparer<T>.Create(Comparison<T>). Bu yöntem bir oluşturmak için kullanılabilirIComparison , anında .

Ayrıca, tehlikeli olma potansiyeline sahip IList<T>olanları yayınlar IList. Gördüğüm çoğu durumda, List<T>uygulamak IListiçin perde arkasında hangi araçlar kullanılıyor?IList<T> , ancak bu garanti edilmez ve kırılgan koda yol açabilir.

Son olarak, aşırı yüklenmiş List<T>.Sort()yöntemin 4 imzası vardır ve bunlardan sadece 2 tanesi uygulanmaktadır.

  1. List<T>.Sort()
  2. List<T>.Sort(Comparison<T>)
  3. List<T>.Sort(IComparer<T>)
  4. List<T>.Sort(Int32, Int32, IComparer<T>)

Aşağıdaki sınıf List<T>.Sort(), IList<T>arabirim için 4 imzanın tümünü uygular :

using System;
using System.Collections.Generic;

public static class IListExtensions
{
    public static void Sort<T>(this IList<T> list)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort();
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort();
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(comparison);
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort(comparison);
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, IComparer<T> comparer)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(comparer);
        }
        else
        {
            List<T> copy = new List<T>(list);
            copy.Sort(comparer);
            Copy(copy, 0, list, 0, list.Count);
        }
    }

    public static void Sort<T>(this IList<T> list, int index, int count,
        IComparer<T> comparer)
    {
        if (list is List<T>)
        {
            ((List<T>)list).Sort(index, count, comparer);
        }
        else
        {
            List<T> range = new List<T>(count);
            for (int i = 0; i < count; i++)
            {
                range.Add(list[index + i]);
            }
            range.Sort(comparer);
            Copy(range, 0, list, index, count);
        }
    }

    private static void Copy<T>(IList<T> sourceList, int sourceIndex,
        IList<T> destinationList, int destinationIndex, int count)
    {
        for (int i = 0; i < count; i++)
        {
            destinationList[destinationIndex + i] = sourceList[sourceIndex + i];
        }
    }
}

Kullanım:

class Foo
{
    public int Bar;

    public Foo(int bar) { this.Bar = bar; }
}

void TestSort()
{
    IList<int> ints = new List<int>() { 1, 4, 5, 3, 2 };
    IList<Foo> foos = new List<Foo>()
    {
        new Foo(1),
        new Foo(4),
        new Foo(5),
        new Foo(3),
        new Foo(2),
    };

    ints.Sort();
    foos.Sort((x, y) => Comparer<int>.Default.Compare(x.Bar, y.Bar));
}

Buradaki fikir, List<T>mümkün olduğunda sıralamayı işlemek için temelin işlevselliğinden yararlanmaktır . Yine, IList<T>gördüğüm çoğu uygulama bunu kullanıyor. Temel alınan koleksiyonun farklı bir tür olması durumunda, List<T>giriş listesindeki öğelerin yeni bir örneğini oluşturmaya geri dönün, sıralamayı yapmak için kullanın, ardından sonuçları giriş listesine geri kopyalayın. Bu, giriş listesi IListarabirimi uygulamasa bile çalışacaktır .


3
try this  **USE ORDER BY** :

   public class Employee
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

 private static IList<Employee> GetItems()
        {
            List<Employee> lst = new List<Employee>();

            lst.Add(new Employee { Id = "1", Name = "Emp1" });
            lst.Add(new Employee { Id = "2", Name = "Emp2" });
            lst.Add(new Employee { Id = "7", Name = "Emp7" });
            lst.Add(new Employee { Id = "4", Name = "Emp4" });
            lst.Add(new Employee { Id = "5", Name = "Emp5" });
            lst.Add(new Employee { Id = "6", Name = "Emp6" });
            lst.Add(new Employee { Id = "3", Name = "Emp3" });

            return lst;
        }

**var lst = GetItems().AsEnumerable();

            var orderedLst = lst.OrderBy(t => t.Id).ToList();

            orderedLst.ForEach(emp => Console.WriteLine("Id - {0} Name -{1}", emp.Id, emp.Name));**

Ya ASC yerine DESC olmasını istiyorsanız?
sam bayt

1

Orijinal gönderide açıklanan sorunun tam olarak çözümünü ararken bu konuyu buldum. Ancak cevapların hiçbiri durumumu tam olarak karşılamadı. Brody'nin cevabı oldukça yakındı. İşte benim durumum ve bulduğum çözüm.

NHibernate tarafından döndürülen aynı türden iki IList'im var ve iki IList'i bire dönüştürdüm, dolayısıyla sıralama ihtiyacı doğdu.

Brody'nin dediği gibi, nesneye (ReportFormat), IList türünün türü olan bir ICompare uyguladım:

 public class FormatCcdeSorter:IComparer<ReportFormat>
    {
       public int Compare(ReportFormat x, ReportFormat y)
        {
           return x.FormatCode.CompareTo(y.FormatCode);
        }
    }

Daha sonra birleştirilmiş IList'i aynı türden bir diziye dönüştürüyorum:

ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList

Ardından diziyi sıralayın:

Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer

Tek boyutlu dizi arabirimi uyguladığından, System.Collections.Generic.IList<T>dizi orijinal IList gibi kullanılabilir.


1

Bu yöntem, listeyi özellik adlarına göre sıralar. Örneği takip edin.

    List<MeuTeste> temp = new List<MeuTeste>();

    temp.Add(new MeuTeste(2, "ramster", DateTime.Now));
    temp.Add(new MeuTeste(1, "ball", DateTime.Now));
    temp.Add(new MeuTeste(8, "gimm", DateTime.Now));
    temp.Add(new MeuTeste(3, "dies", DateTime.Now));
    temp.Add(new MeuTeste(9, "random", DateTime.Now));
    temp.Add(new MeuTeste(5, "call", DateTime.Now));
    temp.Add(new MeuTeste(6, "simple", DateTime.Now));
    temp.Add(new MeuTeste(7, "silver", DateTime.Now));
    temp.Add(new MeuTeste(4, "inn", DateTime.Now));

    SortList(ref temp, SortDirection.Ascending, "MyProperty");

    private void SortList<T>(
    ref List<T> lista
    , SortDirection sort
    , string propertyToOrder)
    {
        if (!string.IsNullOrEmpty(propertyToOrder)
        && lista != null
        && lista.Count > 0)
        {
            Type t = lista[0].GetType();

            if (sort == SortDirection.Ascending)
            {
                lista = lista.OrderBy(
                    a => t.InvokeMember(
                        propertyToOrder
                        , System.Reflection.BindingFlags.GetProperty
                        , null
                        , a
                        , null
                    )
                ).ToList();
            }
            else
            {
                lista = lista.OrderByDescending(
                    a => t.InvokeMember(
                        propertyToOrder
                        , System.Reflection.BindingFlags.GetProperty
                        , null
                        , a
                        , null
                    )
                ).ToList();
            }
        }
    }

0

İşte daha güçlü yazmanın kullanıldığı bir örnek. Yine de en iyi yol olup olmadığından emin değilim.

static void Main(string[] args)
{
    IList list = new List<int>() { 1, 3, 2, 5, 4, 6, 9, 8, 7 };
    List<int> stronglyTypedList = new List<int>(Cast<int>(list));
    stronglyTypedList.Sort();
}

private static IEnumerable<T> Cast<T>(IEnumerable list)
{
    foreach (T item in list)
    {
        yield return item;
    }
}

Cast işlevi, normal bir statik yöntem olarak yazılan 3.5 ile birlikte gelen genişletme yönteminin yalnızca bir yeniden uygulamasıdır. Ne yazık ki oldukça çirkin ve ayrıntılı.


0

VS2008'de, servis referansına tıklayıp "Servis Referansını Yapılandır" ı seçtiğimde, istemcinin servisten döndürülen listeleri nasıl serileştireceğini seçme seçeneği var.

Bilhassa System.Array, System.Collections.ArrayList ve System.Collections.Generic.List arasından seçim yapabilirim.


0
using System.Linq;

var yourList = SomeDAO.GetRandomThings();
yourList.ToList().Sort( (thing, randomThing) => thing.CompareThisProperty.CompareTo( randomThing.CompareThisProperty ) );

Bu çok güzel! Getto.


0

Bu konuda iyi bir yazı buldum ve paylaşacağımı düşündüm. Buradan kontrol edin

Temelde.

Aşağıdaki sınıfı ve IComparer Sınıflarını oluşturabilirsiniz

public class Widget {
    public string Name = string.Empty;
    public int Size = 0;

    public Widget(string name, int size) {
    this.Name = name;
    this.Size = size;
}
}

public class WidgetNameSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
        return x.Name.CompareTo(y.Name);
}
}

public class WidgetSizeSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
    return x.Size.CompareTo(y.Size);
}
}

Sonra bir IListiniz varsa, bunu şöyle sıralayabilirsiniz.

List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));

widgets.Sort(new WidgetNameSorter());
widgets.Sort(new WidgetSizeSorter());

Ama daha fazla bilgi için bu siteye göz atın ... BURAYA göz atın


0

Bu geçerli bir çözüm mü?

        IList<string> ilist = new List<string>();
        ilist.Add("B");
        ilist.Add("A");
        ilist.Add("C");

        Console.WriteLine("IList");
        foreach (string val in ilist)
            Console.WriteLine(val);
        Console.WriteLine();

        List<string> list = (List<string>)ilist;
        list.Sort();
        Console.WriteLine("List");
        foreach (string val in list)
            Console.WriteLine(val);
        Console.WriteLine();

        list = null;

        Console.WriteLine("IList again");
        foreach (string val in ilist)
            Console.WriteLine(val);
        Console.WriteLine();

Sonuç şuydu: IList B A C

A B C Listesi

IList tekrar A B C


1
Gerçekten bir Liste <T> ise geçerlidir. Bazı durumlarda, aşağı yayının çalışmayacağı IList <T> uygulayan başka türleriniz (örneğin, düz bir dizi) vardır. Sort () yönteminin IList <T> için bir genişletme yöntemi olmaması çok kötü.
Cygon

0

Bana sorarsan bu ÇOK DAHA BASİT görünüyor. Bu benim için mükemmel çalışıyor.

Cast () 'yi IList olarak değiştirmek için kullanabilirsiniz, sonra OrderBy ()' yi kullanabilirsiniz:

    var ordered = theIList.Cast<T>().OrderBy(e => e);

NEREDE T türü örneğin. Model.Employee veya Plugin.ContactService.Shared.Contact

Daha sonra for döngüsünü ve onun DONE'unu kullanabilirsiniz.

  ObservableCollection<Plugin.ContactService.Shared.Contact> ContactItems= new ObservableCollection<Contact>();

    foreach (var item in ordered)
    {
       ContactItems.Add(item);
    }

-1

Senin dönüştürün IListINTO List<T>veya başka jenerik koleksiyonu ve sonra kolayca sorgu / sıralama bu kullanarak yapabilirsiniz System.Linqad (o uzatma yöntemlerinin demet tedarik edecek)


9
IList<T>uygular IEnumerable<T>ve bu nedenle Linq işlemlerini kullanmak için dönüştürülmesine gerek yoktur.
Steve Guidi
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.