开发者

Using IComparer<> with delegate function to search

开发者 https://www.devze.com 2023-01-06 01:49 出处:网络
This feels like a too easy question to be found with google, I think/hope I\'ve got stuck in the details when trying to implement my own version of it. What I\'m trying to do is to sort a list of MyCl

This feels like a too easy question to be found with google, I think/hope I've got stuck in the details when trying to implement my own version of it. What I'm trying to do is to sort a list of MyClass objects depending on my Datatype object different search functions should be used.

I've had something like this in mind for the class Datatype:

class Datatype {
  public delegate int CMPFN(object x, object y);
  private CMPFN compareFunction;

  (...)

  private XsdDatatype((...), CMPFN compareFunction) {
      (...)
      this.compareFunction = compareFunction;
  }

  public CMPFN GetCompareFunction() {
    return this.compareFunction;
  }

  static private int SortStrings(object a, object b) {
      return ((MyClass)a).GetValue().CompareTo(((MyClass)b).GetValue());
  }
}

And later on I'm trying to sort a MyClass list something like this:

List<MyClass> elements = GetElements();
Datatype datatype = new Datatype((...), Datatype.SortStrings);
elements.Sort(datatype.GetCompareFunction()); // <-- Compile error!

I'm not overly excited about the cast in Datatype.SortStrings but it feels like this could work(?). The compiler however disagrees and gets me this error on the last line above and I'm a bit unsure exactly why CMPFN can't be converted/casted(?) to IComparer.

Cannot convert type 'proj.Datatype.CMPFN' to 'System.Collections.Generic.IComparer<proj.MyClass>'开发者_如何学C


Delegates aren't duck-typed like that. You can create an Comparison<MyClass> from a CMPFN but you can't use a plain reference conversion - either implicit or explicit.

Three options:

  • Create the comparer like this:

    elements.Sort(new Comparison<MyClass>(datatype.GetCompareFunction()));
    
  • Use a lambda expression to create a Comparison<T> and use that instead:

    elements.Sort((x, y) => datatype.GetCompareFunction()(x, y));
    
  • Write an implementation of IComparer<MyClass> which performs the comparison based on a CMPFN

Note that the second approach will call GetCompareFunction once per comparison.

A much better solution would be to get rid of CMPFN entirely - why not just use (or implement) IComparer<MyClass> to start with? Note that that would remove the casts as well. (If you're happy using delegates instead of interfaces, you could express the comparison as a Comparison<MyClass> instead.)

Note that as of .NET 4.5, you can use Comparer.Create to create a Comparer<T> from a Comparison<T> delegate.

I'm not sure why your current API is in terms of object, but you should be aware that in C# 3 and earlier (or C# 4 targeting .NET 3.5 and earlier) you wouldn't be able to convert an IComparer<object> into an IComparer<MyClass> (via a reference conversion, anyway). As of C# 4 you can, due to generic contravariance.


There are a number of overloads of List<T>.Sort, but there are none which take a delegate with the parameters you have defined (two objects).

However, there is an overload that takes a Comparison<T> delegate, which you can work with your code with a few minor modifications. Basically, you just replace your CMPFN delegate with Comparison<MyClass> - as an added bonus, you get strong-typing in your SortStrings function, too:

static private int SortStrings(MyClass a, MyClass b) {
    return a.GetValue().CompareTo(b.GetValue());
}

public Comparison<MyClass> GetCompareFunction() {
    return SortStrings; // or whatever
}

...

elements.Sort(datatype.GetCompareFunction()); 


Try something like this

class AttributeSort : IComparer<AttributeClass >
    {
        #region IComparer Members

        public int Compare(AttributeClass x, AttributeClass y)
        {                
            if (x == null || y == null)
                throw new ArgumentException("At least one argument is null");

            if (x.attributeNo == y.attributeNo) return 0;
            if (x.attributeNo < y.attributeNo) return -1;
            return 1;
        }

        #endregion
    }

You can call it then like this

List<AttributeClass> listWithObj ....

listWithObj.Sort(new AttributeSort());

Should work like you want. You can create a type-safe comparer class as well.

0

精彩评论

暂无评论...
验证码 换一张
取 消