I'm trying to convert from some Java code, and I've got one massively complicated headache. Bear with me while I see if I can provide a comprehensible explanation.

Let's suppose I have an abstract class called BaseDatatype. Other classes will inherit from this, e.g. StringDatatype, NumericDatatype and so on.

Now, all datatypes should be comparable, but I'd like to have different methods for comparing same types.

So, I write this interface:

interface IDataComparable <X> where X : BaseDatatype
{
    CompareResult compareAnyType( BaseDatatype instance );

    CompareResult compareSameType( X instance );
}

So far, so good.

Now for the top level implementation, which will be abstract. In C#, abstract classes must declare interface methods, so we must have:

abstract class BaseDatatype : IDataComparable<BaseDatatype>
{
    abstract CompareResult compareAnyType( BaseDatatype instance );

    abstract CompareResult compareSameType( BaseDatatype instance );
}

But this misses the point. What I really want to enforce is having to write:

class StringDatatype : BaseDatatype
{
    CompareResult compareAnyType( BaseDatatype instance )  { ... }

    CompareResult compareSameType( StringDatatype instance )  { ... }
}

(note the use of StringDatatype) but I can't see how that would happen.

I could forgo this particular behaviour, but this is only part of the problem - there are a lot more reasons for wanting this related to gaining any benefit from generics.

As for how I did it in Java, it's fairly complex, if not plain horrible.

public abstract class BaseDatatype< X extends BaseDatatype < ? > > implements DataComparable<X>
{
//not forced to implement methods
}
public interface DataComparable<X extends BaseDatatype< ? >>
{
    CompareResult compareAnyType( BaseDatatype< ? > instance );

    CompareResult compareSameType( X instance );
}
public class StringData extends BaseDatatype<StringData>
{
    CompareResult compareAnyType( BaseDatatype< ? > instance ) { ... }

    CompareResult compareSameType( StringData instance ) { ... }
}

i.e. recursive generics (which C# has) and unbounded wilcards (which it doesn't). Charming eh!

Does this make the slightest bit of sense? Any clues?

Thanks!

The override modifier is required to extend or modify the abstract or virtual implementation of an inherited method.

Hello, you have interesting problem ... let's see:

Does this make the slightest bit of sense?

Well, that depends on from which view to look at this:

  1. It sounds logical, when you declare in base class, that it or it's children would implement DataComparable. But since the base class is abstract - we're not forced to implement everything "right here and right now". Especially, considering, that you gonna compare some child types and base class shouldn't be aware of it's children.
  2. From other view - the base class contains all common and necessary
    members, that should be in the child classes. Considering, that in your case the base class is abstract, it can provide a full realization of it's methods or let the child classes implement them by itself. So it would be a bit confusing to gather all information about the base class in it's inheritance hierarchy. That's probably why you should provide at least signature of all non implemented yet members.

Now - closer to your problem. Indeed, your variant of that java piece of code look a bit strange. So we must to redesign that a bit...
Just to be sure .. are you sure, that for your case StringDatatype and NumericDatatype should have same base class? (Even in C# they derived from System.Object and System.ValueType respectively). Maybe they have more differences, than commons? .. Or maybe just problems in an example ..
Ok, if inheritance from that base class is a "must have" - I can provide you the folowing approach:

Avoiding explicit force ... (or maybe just leave there "Base vs. Base" comparison)

class StringDatatype : BaseDatatype, IComparable<NumericDatatype>, IComparable<BaseDatatype>
    {
        public int CompareTo(NumericDatatype other)
        {
            //...
        }

        public int CompareTo(BaseDatatype other)
        {
            //...
        }
    }

//and the base ...
    abstract class BaseDatatype: IComparable<BaseDatatype>
    {
        public CompareResult CompareTo(BaseDatatype other)
        {
            //...
        }
    }

Now. what we have:

  • Base class has no idea about StringDatatype and other children.
  • There's a default behaviour for the children of your base class if they don't implement IComparable .
  • Everything is still strong typed.

But still, we have a strong enough cohesion between child classes, that would be compared. So the next step would be avoiding it .. It might be some method or property, that would be describe the child class for others, so that you could compare them. E.g.:

interface INumericDescriptor
    {
        int Descriptor { get; }
    }

Then we should implement that in our classes:

abstract class BaseDatatype : INumericDescriptor
    {
        public abstract int Descriptor{get;}
    }

    class StringDatatype : BaseDatatype
    {
        public string text = "Hi there";

        public override int Descriptor
        {
            get { return text.Length; }
        }
    }

    class NumericDatatype:BaseDatatype
    {
        public int value = 4;

        public override int Descriptor
        {
            get { return value; }
        }        
    }

Then to compare that we should extend our base class to be comparable:

abstract class BaseDatatype : INumericDescriptor, IComparable<INumericDescriptor>
    {
        public abstract int Descriptor{get;}

        public int CompareTo(INumericDescriptor other)
        {
            return Descriptor.CompareTo(other);
        }
    }

So now the cohesion is gone. You can use more descriptors, or placing the "CompareTo" part in any other class, or anything else .. that were just a thoughts in a loud :)

This article has been dead for over six months. Start a new discussion instead.