2008-10-22 11 views
5

J'ai un IList<T> que je dois trier, et je préférerais ne pas copier la liste si possible. Je l'ai remarqué que ArrayList a une méthode statique Adapter qui enveloppe la liste passée sans le copier, mais cela prend un IList et j'ai un IList<T>. Est-il sûr de jeter d'un System.Collections.Generic.IList<T> à un System.Collections.IList et il suffit d'utiliser la méthode Adapter?Quelle est la meilleure façon de trier un IList <T> dans .Net 2.0?

Notez que ceci est .Net 2.0, LINQ est pas une option.

Répondre

13

Depuis le blog de Paul Fox, je recommande le poste « Comment trier un IList »: http://foxsys.blogspot.com/2007/06/how-to-sort-generic-ilist.html

Juste au cas où ce blog va loin dans l'avenir, je vais copier le message ici:


Comment trier un IList

générique Mise à jour

Vous pouvez lire et mis à jour abou post t sorting generic IList and List. Beaucoup de gens préféreront les méthodes mentionnées dans le post mis à jour.

Tri un IList

générique je tente de trier un IList < générique> et trouvé un moyen assez simple de le faire.

Étape 1

Vous devez implémenter IComparable pour le type contenu dans votre IList. Pour cet exemple, je vais utiliser une simple classe Language Dto.

public class LanguageDto : IComparable { 
private String name; 
public string Name { get { return name; } set { name = value; } } 

public LanguageDto(string name) { 
    this.name = name; 
} 

#region IComparable Members 
public int CompareTo(object obj) { 
    if (obj is LanguageDto) { 
    LanguageDto language = (LanguageDto)obj; 
    return this.name.CompareTo(language.name); 
    } 
    throw new ArgumentException(string.Format("Cannot compare a LanguageDto to an {0}", obj.GetType().ToString())); 
} 
#endregion 
} 

ÉTAPE 2

Trier votre IList. Pour ce faire, vous utiliserez la méthode ArrayList.Adapter() en passant dans votre IList, puis en appelant la méthode Sort. Comme si ...

ArrayList.Adapter((IList)languages).Sort(); 

Note: Les langues est de type "IList"

langues devrait alors une liste triée de votre type!

+2

Alors il ne redore juste le IList à un IList. Pourquoi est-ce un casting sûr à faire? –

+0

Ne pas copier tout le message. Il semble que la bonne chose à faire pour s'assurer que le contenu est préservé, mais la copie de l'ensemble du poste nécessite probablement une autorisation explicite du détenteur des droits d'auteur. Vous pouvez et devriez créer un résumé, cependant. –

+0

@EddieDeyo: Je sais que c'est vieux de quelques années, mais c'est parce que IList n'existe pas vraiment, mais c'est un IList de LanguageDto, qui est bien sûr un IList de LanguageDto. T est (principalement) une illusion de temps de compilation. La "différence" entre un IList et IList est que pour IList Item [0] retourne un Object, mais pour IList Item [0] retourne un Object qui sera toujours un T, et si T est un struct/primitive l'Object n'aura pas été emballé. Ainsi, une distribution de IList à IList serait sûre (inutile, mais sûre) mais l'inverse n'est pas – jmoreno

6

Vous ne pouvez pas lancer IList (T) à IList.

Après quelques reniflant avec réflecteur, il semble que ArrayList.Adapter (IList) .Sort() va d'abord copier la liste à un tableau d'objets, trier le tableau, puis copiez le tableau arrière à une liste:

object[] array = new object[count]; 
this.CopyTo(index, array, 0, count); 
Array.Sort(array, 0, count, comparer); 
for (int i = 0; i < count; i++) 
{ 
    this._list[i + index] = array[i]; 
} 

Vous pouvez obtenir les frais généraux de la boxe si T dans votre liste (T), un type de valeur.

Si vous avez besoin de modifier la séquence des objets dans la liste que vous avez, vous pouvez le faire de la même:

IList<object> unsorted = ... 
List<object> sorted = new List<object>(unsorted); 
sorted.Sort(); 
for (int i = 0; i < unsorted.Countt; i++) 
{ 
    unsorted[i] = sorted[i]; 
} 

Si la liste est si énorme (comme dans des centaines de millions d'articles) que vous Je ne peux pas faire une copie supplémentaire en mémoire, je suggère d'utiliser une liste (T) en premier lieu ou de mettre en œuvre votre algorithme de tri sur place préféré.

+0

Ouais, mais j'avais dit que j'espérais éviter de copier la liste. –

+0

ne pas le copier alors et juste dire: unsorted = trié; –

+0

@Hallgrim: bien sûr que vous pouvez. http://ideone.com/bqzIpk4 'IList x = new int [] {1,2,3, 4}; IList g = (IList ) x; System.Console.WriteLine (g.Count); IList g2 = g comme IList; System.Console.WriteLine (g2.Count); ' – jmoreno

0

Je sais que ce n'est pas .NET 2.0 mais j'aime LINQ tant et approuvera toutes les chances que je reçois :)

Trier simple:

var sortedProducts = 
    from p in products 
    orderby p.ProductName 
    select p; 

ObjectDumper.Write(sortedProducts); 

Trier par de multiples conditions :

string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; 

var sortedDigits = 
    from d in digits 
    orderby d.Length, d 
    select d; 

Les deux exemples de 101 Linq Samples

+1

C'est bon. Grâce à la magie de google, il est probable que quelqu'un arrivera ici à la recherche d'aide qui ne partage pas la contrainte 2.0 de l'OP. –

1

Puisque la méthode de tri n'est pas sur l'interface IList vous pouvez envisager de créer votre propre:

interface ISortableList<T> : IList<T> 
{ 
    void Sort(); 
    void Sort(IComparer<T> comparer); 
} 

class SortableList<T> : List<T>, ISortableList<T> { } 

/* usage */ 
void Example(ISortedList<T> list) 
{ 
    list.Sort(); 
    list.Sort(new MyCustomerComparer()); 
} 

En général, le type de paramètre que vous indiquez dans votre méthode doit être le plus petit dénominateur commun des membres que vous avez réellement besoin appeler. Si vous avez vraiment besoin d'appeler la méthode Sort() alors votre paramètre doit avoir ce membre défini. Sinon, vous devriez probablement le charger dans un autre objet qui peut faire ce que vous voulez, tels que:

void Example(IList<T> list) 
{ 
    list = new List<T>(list).Sort(); 
} 

Cela devrait effectivement être assez rapide, presque certainement encore plus rapide que d'écrire votre propre algorithme de tri en ligne personnalisé.

-3
IList<object> unsorted = ... 
IList<object> sortedList = unsorted.Orderby(x => x.Tostring()).Tolist(); 

cela donnera la liste triée sur le champ particulier de l'objet.

0

Si vous avez besoin de trier les listes (pas ILists) de classes différentes sans avoir besoin de créer une classe de tri séparée pour chacune d'entre elles tout en gardant vos classes d'entités propres (vous ne voulez pas implémenter IComparable), vous pouvez utiliser les éléments suivants (compatible avec .NET 2.0):

public class DynamicComparer<T> : IComparer<T> 
{ 

    private Func<T, int> calculateFunc; 
    private int calculateMultiplier; 

    private Func<T, T, int> compareFunc; 
    public DynamicComparer(Func<T, int> calculateFunc, bool reverse = false) 
    { 
     if (calculateFunc == null) 
     { 
      throw new Exception("Delegate function 'calculateFunc' cannot be null."); 
     } 

     this.calculateFunc = calculateFunc; 
     this.calculateMultiplier = reverse ? -1 : 1; 
     this.compareFunc = null; 
    } 

    public DynamicComparer(Func<T, T, int> compareFunc) 
    { 
     if (calculateFunc == null) 
     { 
      throw new Exception("Delegate function 'compareFunc' cannot be null."); 
     } 

     this.calculateFunc = null; 
     this.compareFunc = compareFunc; 
    } 

    public int Compare(T x, T y) 
    { 
     if (calculateFunc != null) 
     { 
      return (calculateFunc(x) - calculateFunc(y)) * this.calculateMultiplier; 
     } 
     if (compareFunc != null) 
     { 
      return compareFunc(x, y); 
     } 

     throw new Exception("Compare not possible because neither a Compare or a Calculate function was specified."); 
    } 
} 

vous aurez également besoin des délégués Func si vous utilisez .NET 2.0 (disponible sur Replacing Func with delegates C#):

public delegate TResult Func<T, TResult>(T t); 
public delegate TResult Func<T, U, TResult>(T t, U u); 

utilisation:

myList.Sort(new DynamicComparer<MyClass>(x => x.MyIntProperty) // Ascending 
myList.Sort(new DynamicComparer<MyClass>(x => x.MyIntProperty, true) // Descending 

Certains tests simples de l'unité:

[TestClass()] 
public class DynamicComparerTU 
{ 
    [TestMethod()] 
    public void SortIntList() 
    { 
     // Arrange 
     dynamic myIntArray = new int[] { 
      4, 
      1, 
      9, 
      0, 
      4, 
      7 
     }; 
     dynamic myIntList = new List<int>(myIntArray); 

     // Act 
     int temp = 0; 
     for (int write = 0; write <= myIntArray.Length - 1; write++) 
     { 
      for (int sort = 0; sort <= myIntArray.Length - 2; sort++) 
      { 
       if (myIntArray(sort) > myIntArray(sort + 1)) 
       { 
        temp = myIntArray(sort + 1); 
        myIntArray(sort + 1) = myIntArray(sort); 
        myIntArray(sort) = temp; 
       } 
      } 
     } 

     myIntList.Sort(new DynamicComparer<int>(x => x)); 

     // Assert 
     Assert.IsNotNull(myIntList); 
     Assert.AreEqual(myIntArray.Length, myIntList.Count); 
     for (int i = 0; i <= myIntArray.Length - 1; i++) 
     { 
      Assert.AreEqual(myIntArray(i), myIntList(i)); 
     } 
    } 

    [TestMethod()] 
    public void SortStringListByLength() 
    { 
     // Arrange 
     dynamic myStringArray = new string[] { 
      "abcd", 
      "ab", 
      "abcde", 
      "a", 
      "abc" 
     }; 
     dynamic myStringList = new List<string>(myStringArray); 

     // Act 
     myStringList.Sort(new DynamicComparer<string>(x => x.Length)); 

     // Assert 
     Assert.IsNotNull(myStringList); 
     Assert.AreEqual(5, myStringList.Count); 
     Assert.AreEqual("a", myStringList(0)); 
     Assert.AreEqual("ab", myStringList(1)); 
     Assert.AreEqual("abc", myStringList(2)); 
     Assert.AreEqual("abcd", myStringList(3)); 
     Assert.AreEqual("abcde", myStringList(4)); 
    } 

    [TestMethod()] 
    public void SortStringListByLengthDescending() 
    { 
     // Arrange 
     dynamic myStringArray = new string[] { 
      "abcd", 
      "ab", 
      "abcde", 
      "a", 
      "abc" 
     }; 
     dynamic myStringList = new List<string>(myStringArray); 

     // Act 
     myStringList.Sort(new DynamicComparer<string>(x => x.Length, true)); 

     // Assert 
     Assert.IsNotNull(myStringList); 
     Assert.AreEqual(5, myStringList.Count); 
     Assert.AreEqual("abcde", myStringList(0)); 
     Assert.AreEqual("abcd", myStringList(1)); 
     Assert.AreEqual("abc", myStringList(2)); 
     Assert.AreEqual("ab", myStringList(3)); 
     Assert.AreEqual("a", myStringList(4)); 
    } 
} 
Questions connexes