1

So, I've got the following problem:

I have a number of ordered elements, ordered in the following way:

  • Type 1: Must be the first element. (only one possible, always present)
  • Type 2: Must be the second and following elements, if present.
  • Type 3: Must be the ordered last.

Stated a bit more succintly / pseudoregex:

12*3+

Now, when constructing these elements, I do so in a method, where it is easy to do something like:

public List<IElement> CreateElements(...)
{
    var list = ...;
    list.Add(new Type1());
    list.AddRange(GetType2Elements());
    list.AddRange(GetType3Elements());
}

However, there is no explicit semantic ordering - it's just a list, that I happen to construct in a certain way.

A more explicit way is to have IElement implement IComparable, and then use an explicitly sorted list and returning that instead. That would carry the "sortedness" out of my construction method, and sort the entities irregardless how they are created.

However, it is suddenly a bit less trivial, since atleast 3 different Compare methods must be implemented.

What would you choose?

3 Answers 3

1

I would solve it like this:

// returns a list of elements, ordered by type 1, type 2 and type 3
public List<IElement> CreateElementsOrdered(...)
{
    ...
}

Since the name and the comment of your function define the contract, every consumer of your method can rely on the fact that the list is ordered in this way. On the other hand, anyone modifying CreateElementsOrdered will now know that changing the order of the elements returned (in a way that breaks the 1 - 2 - 3 order) will break backwards compatibility.

1
  • This is the best answer because it is by far the simplest.
    – Mike Nakis
    Commented Dec 10, 2011 at 15:05
1

Wouldn't a simple class be better here than a list? You can create a class which has the three elements in it and then a constructor/factory method to create it in the required order.

For example:

public class Wrapper
{
    public Type1 MainObject { get; set; }
    public List<Type2> SubElements { get; set; }
    public List<Type3> OtherElements { get; set; }
}

public Wrapper() { }

public static Wrapper FactoryCreateOrdered()
{
    return new Wrapper
    {
        MainObject = new Type1();
        SubElements = //null if not present or a list of Type2s
        OtherElements = GetType3Elements();
    };
}

This is a much more obvious structure than dealing with a list which might have a null at position 2 but data at positions 3 onwards. Putting this in a list is asking for exceptions in production because there will be one code path which is rarely used, doesn't have great/any unit tests and the developer didn't expect an element in his list to be null.

Edit: I got a little distracted from the question which was about sorting but now that you have a custom class, you can apply whatever sorting criteria you want to and it's going to be miles easier than trying to fudge a list to work for you.

2
  • Actually, the reason I am putting it in a list is so that it's convenient and easy to use 'foreach element in list: element.consume'. But you might be right, I'll have to think about using Wrapper with a method consume instead. The thing is though, this already happens in an internal architecture, in a method similar to consume. I'll think on it a bit, and then perhaps accept. If you don't have any other comment? :)
    – Max
    Commented Dec 10, 2011 at 12:10
  • Adding the consume method to the Wrapper class sounds perfectly sensible. I have to say though I'm really not sure about using a foreach construct over this sort of object - it really doesn't sound like it's designed to do that. My guess is that you do one thing with Type1, a different thing with Type2 and so on. If this is the case, your wrapper class can then provide public methods for all of those things and you can hide away the details of it. Commented Dec 10, 2011 at 16:54
0

Since you wrote in your comment that you mainly need the list for a foreach loop, let me suggest an alternative approach. Why not just return an (immutable) IEnumerable instead of a (mutable) List?

public IEnumerable<IElement> CreateElements()
{
    yield return new Type1();
    foreach (var e in GetType2Elements())
        yield return e;
    foreach (var e in GetType3Elements())
        yield return e;
}

Since the IEnumerable created by yield is immutable, the order can never change. However, it would still be possible to do

foreach (var e in CreateElements())
    e.consume();

Note, though, that the behavior changes slightly due to deferred execution:

var elements = CreateElements();  // Elements have not been created yet!
foreach (var e in elements)       // New Type1 is executed as soon as the first
    e.consume();                  // element is accessed.

If you go for stu's wrapper, you could add a custom enumerator as follows:

public class Wrapper : IEnumerable<IElement>
{ 
    ....

    public IEnumerator<IElement> GetEnumerator()
    {
        yield return MainObject;
        foreach (Type2 e in SubElements)
            yield return e;
        foreach (Type3 e in OtherElements)
            yield return e;

    } 

    // required since IEnumerable<T> inherits non-generic IEnumerable
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

This would allow you to do

foreach (var e in myWrapper)
    e.consume();

Since myWrapper never exposes its enumeration as a list, it can never be reordered.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.