0

I am trying to implement a generic comparer (for a sort) for all objects that have a Text property... so two ASP.net textboxes can be compared, two labels or in this specific case two RadTreeNodes in a telerik RadTreeView (as long as they have a Text property). So I've put the following together to try and so this but get an error as follows:

I have the following code:

public class TextComparer<T> : IComparer
    where T : IHasTextProperty
    {
        public int Compare(object a, object b)
        {
            T nodeA = (T)a;
            T nodeB = (T)b;
            return nodeA.Text.CompareTo(nodeB.Text);
        }
    } 

    public interface IHasTextProperty
    {
        string Text { get; set; }
    }

Then plan on using it like so...

Array.Sort(nodes, new TextComparer<RadTreeNode>());

but get the following message :

Error 6613 The type 'Telerik.Web.UI.RadTreeNode' cannot be used as type parameter 'T' in the generic type or method 'TextComparer'. There is no implicit reference conversion from 'Telerik.Web.UI.RadTreeNode' to 'IHasTextProperty'

I'm sure this is a simple fix, but I'm just a little stumped as to how to fix it.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Craig
  • 849
  • 8
  • 21
  • 2
    The fix might not be too simple - what you are trying to do is to use an "implicit interface", a feature that is not supported by C# or the CLI. Only classes that, upon declaration, ecplicitly indicate that they implement `IHasTextProperty` are assignment-compatible to fields of type `IHasTextProperty`. Classes such as ASP.NET text boxes that simply expose a `Text` property, but do not implement the interface, are not compatible. – O. R. Mapper Sep 08 '15 at 12:02

2 Answers2

3

You are attempting to perform duck-typing in C#, which doesn't support duck-typing. In some languages, you can match a type based on it having a certain property, such as Text in this case. This only works if the language supports this technique.

With C#, a class must explicitly implement an interface for it to be deemed to have that interface's type. Telerik.Web.UI.RadTreeNode doesn't implement IHasTextProperty. T is contrained to types that implement IHasTextProperty, and so you get the error you see.

You really can't use generics in this case. You need to test whether a and b have a Text property. This can be done using reflection or by using dynamic. Neither solution will be as neat as what you were attempting to do unfortunately.

David Arno
  • 42,717
  • 16
  • 86
  • 131
  • Thank you... this has been a great help... I've had to move away from generics. Both this and the answer below are collectively correct answers... Thanks again! – Craig Sep 08 '15 at 15:10
1

System.Web.UI provides its own IHasTextProperty namely ITextControl (msdn), which behaves exactly like your IHasTextProperty. The downside is that you can not be sure RadTreeNode (or any other 3rd party control) implements this interface.

The only way to be sure is to remove this check from compile time and put it into runtime via reflection, which is quite simple but perhaps not what you want. In case you still want to use it here's an example using an ArgumentException in the TextComparer's constructor to ensure only valid objects are compared.

public class TextComparer<T> : IComparer
{
    private bool HasTextProperty(Type t)
    {
        return (t.GetProperty("Text", typeof(string)) != null);
    }

    private string GetTextPropertyValue(object obj)
    {
        return obj.GetType().GetProperty("Text", typeof(string)).GetValue(obj) as string;
    }

    public TextComparer()
    {
        if (!HasTextProperty(typeof(T))) throw new ArgumentException(string.Format("{0} doesn't provide a Text property", typeof(T).Name), "T");
    }

    public int Compare(object x, object y)
    {
        return GetTextPropertyValue(x).CompareTo(GetTextPropertyValue(y));
    }
}
Sors
  • 494
  • 1
  • 4
  • 12
  • Thanks for the example... so close! As you predicted, I'm not able to use the ITextControl which is a shame... but your example above is great and very much appreciated, thank you! – Craig Sep 08 '15 at 15:13