views:

175

answers:

3

I've got a custom object (example only code for ease of understanding) ...

public class MyClass
{
    private string name;
    private int increment;
    private Guid id;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Increment
    {
        get { return increment; }
        set { increment = value; }
    }

    public Guid Id
    {
        get { return id; }
        set { id = value; }
    }
}

... and a custom collection of this class ...

public class MyClassCollection : Collection<MyClass>
{

}

I was looking to write a Sort routine for the collection which will have the following public method ...

    public void Sort(params string[] sortProperties)
    {
        if (sortProperties == null)
        {
            throw new ArgumentNullException("sortProperties", "Parameter must not be null");
        }

        if ((sortProperties.Length > 0) && (Items.Count > 1))
        {
            foreach (string s in sortProperties)
            {
     // call private sort method
                Sort(s);
            }
        }
    }

... and the private Sort method would take a parameter of the property name ...

    private void Sort(string propertyName)
    {

    }

What I want to do is be able to pass in a set of property names into the method ...

        MyClassCollection current = new MyClassCollection();

        // setup a objects in the collection
        current = GetCollectionData();

        // sort by Name, then by Increment
        current.Sort("Name", "Increment");

Using the property names passed into the method I want to be able to check to see if it has a property of that name, if so work out what type it is and then run through a sort of it.

The interim workaround which I have currently got is ...

    private void Sort(string propertyName)
    {
        // convert to List
        List<MyClass> myCurrentClass = Items as List<MyClass>;

        // sort
        if (myCurrentClass != null)
        {
            switch (propertyName)
            {
                case "Name":
                    myCurrentClass.Sort(delegate(MyClass myClassOne, MyClass myClassTwo)
                                 {
                                     return
                                         Comparer<string>.Default.Compare(myClassOne.Name,
                                                                          myClassTwo.Name);
                                 }
                        );
                    break;

                case "Increment":
                    myCurrentClass.Sort(delegate(MyClass myClassOne, MyClass myClassTwo)
                                 {
                                     return
                                         Comparer<int>.Default.Compare(myClassOne.Increment,
                                                                       myClassTwo.Increment);
                                 });
                    break;
            }
        }
    }

... but ideally I would like to switch on the underlying type of the Property (string, int etc.) and using a distinct number of delegate calls for the types for sorting. I've looked around but I've not found anything which points me in the right direction. I've had a look at reflection but I couldn't see anything which would be able to help me.

Is this even possible? and if so, how?!

Cheers!

+2  A: 

Reflection would be the way to go - look at Type.GetProperty(string name). Creating the right comparer might be tricky after that - you might want to write a generic method, and then invoke that with reflection based on the property type. It all gets pretty icky, I'm afraid - but it's definitely feasible.

Jon Skeet
Couldn't you use the regular , non-generic Comparer which takes objects as arguments ?
Frederik Gheysels
Possibly. can't say I've ever used the non-generic form - didn't even know of its existence!
Jon Skeet
+1 Thanks for your pointers. Yours along with Frederik's have managed to get me through :-)
WestDiscGolf
+2  A: 
private void Sort( string propertyName )
{
   List<MyClass> myCurClass = ...

   myCurClass.Sort(delegate( MyClass left, MyClass right ){

      PropertyInfo lp = typeof(MyClass).GetProperty (propertyName);

      Comparer.Default.Compare (pi.GetValue(left), pi.GetValue(right));


   });
}

I think this should get you started. :) (Not tested, nor compiled, but you'll get the idea)

Frederik Gheysels
+1 Thanks for your pointers! Let me know what you think of the solution I came up with.
WestDiscGolf
A: 

After hitting my head against the problem for a while and hoping on a train home last night I decided that I would try and bash out an answer. Using a combination of Jon's pointers and Frederik's use of the PropertyInfo class and keeping the original idea of switching on the underlying object type, this is what I came up with ...

    private void Sort_version2(string propertyName)
    {
        // convert to list
        List<MyClass> myCurrentClass = Items as List<MyClass>;

        string typeOfProperty;
        PropertyInfo pi;

        // sort
        if ((myCurrentClass != null) && (MyClass.HasDetailAndExtract(propertyName, out typeOfProperty, out pi)))
        {
            switch(typeOfProperty)
            {
                case "System.String":
                    myCurrentClass.Sort(delegate(MyClass one, MyClass two)
                                            {
                                                return
                                                    Comparer<string>.Default.Compare(pi.GetValue(one, null).ToString(),
                                                                                     pi.GetValue(two, null).ToString());
                                            });
                    break;

                case "System.Int32":
                    myCurrentClass.Sort(delegate (MyClass one, MyClass two)
                                            {
                                                return
                                                    Comparer<int>.Default.Compare(
                                                        Convert.ToInt32(pi.GetValue(one, null)),
                                                        Convert.ToInt32(pi.GetValue(two, null)));
                                            });
                    break;
                default:
                    throw new NotImplementedException("Type of property not implemented yet");
            }
        }
    }

I've documented the thought process and more details on my blog let me know what you think!

Thanks to Jon and Frederik for the help :-)

WestDiscGolf