0

I just hit the very basic problem in OOP and I cannot see any working solution except postponing appropriate check until run time.

It is pretty clear notion of an action "do something with value of the same type" no matter what means given language provides. Consider Equals in C# which in simplest form is "is this object is equal to that object". Or Comparable<T> also in C#. No matter where you start such hierarchy the following is valid:

Comparable<T> --> Animal --> Cat
                       +---> Dog

And the problem:

Animal cat = new Cat();
Animal dog = new Dog();
cat.Compare(dog); // ERR

How to design a language from scratch in such way, that the above line would give an error in compile time?

So far I introduced type Self, which gives clear meaning what you would like to do within a type:

type Animal ... compare(cmp Self) ... 
type Cat ... compare(cmp Self) ...
type Dog ... compare(cmp Self) ...

but it is not enough to avoid the problem with cross passing objects.

Can it be done? Maybe it was already done? How?

I already considered such extreme measures as two kinds of inheriting methods -- with virtual mechanism and not -- and calling them appropriately, but this in turn excludes the generic types (unless generic types are implemented as in C++ -- as templates). With only 2 level of inheritance it could be done by forbidding using top level type except for generic constraints -- of course it is too limiting.

greenoldman
  • 1,533

1 Answers1

3

Just to clarify, IComparable<T> defines that an object has a CompareTo(T) method that can be called.

It does not require that the object (on which the method was called) and the argument (that was passed into the method) are of the same most derived type.

In general, if you call IComparable<T> between a Cat and a Dog, it should return false.

This is consistent with the behavior of the non-generic, non-interface Object.Equals(Object).

In fact, if someone passes in ((Animal)null) as the argument (null, as in non-existence, does not have a most defined type), given that the method target (the object on which the method is called) is non-null, it should also return false.

There are several approaches (not exhaustive). These are not rules or guidelines - these are options for anyone to consider.

Approach 1 (at compile time)

  • Animal should not inherit from an abstract class Comparable.
    As a result, it would not be comparable - it would only have the most basic Object.Equals(Object) method.
  • Instead, only the most derived types, namely Cat and Dog, should implement the corresponding interface IComparable<Cat> and IComparable<Dog>, respectively.
  • This means it would not be possible to call IComparable<T>.CompareTo(T) when given two Animal instances without performing additional type comparison (see below) and type cast.

Approach 2 (at runtime)

Use the following code as a comparison pre-condition:

if (!left.GetType().Equals(right.GetType()))
{
    return false;
}
// followed by the normal comparison logic

This is because in C#, Object.GetType() returns the runtime type of the object.

rwong
  • 17,140