Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow implementing classes to use themselves as types

Tags:

c#

I am just wandering if the following is possible in C#.

Let's say I have a List-like interface that mandates the operation reverse. I.e.:

public interface ReversableList<T> {
      // Return a copy of this list but reversed
      ReversableList<T> Reverse();

      // We still need to return the contained type sometimes
      T Get(int index)
}

However, now when I go and implement that interface:

public class ListImplementation<T> : ReversableList<T> 
      ReversableList<T> Reverse();   
}

I am still forced to use the interface ReversableList. So even if the user were to instantiate my ListImplementation directly, they are still forced to deal with ReversableList when calling the Reverse method:

var concreteList = new ListImplementation<String>();
ReversableList<T> reversed = concreteList.Reverse(); 

whereas what I had in mind was that Reverse() of the ListImplementation will be another ListImplementation, like so:

var concreteList = new ListImplementation<String>();
ListImplementation<String> reversed = concreteList.Reverse();

while still implementing a generic enough interface.

If this is confusing, let me know and I will clarify/add any necessary details or corrections. Thank you!

Source of interest

What follows is the explanation for why this is interesting to me.

I followed the function called reversed in the scala's collections from the List (which is a concrete type) through all the interfaces (traits) that it inherits.

What I found is very curious.

The original reverse method is defined abstractly on the GenSeqLike trait.

trait GenSeqLike[+A, +Repr] ...
    def reverse: Repr   // <--- NO BODY, means abstract


Then, it is defined concretely in a trait SeqLike that extends GenSeqLike trait:

trait SeqLike[+A, +Repr] extends GenSeqLike[A, Repr]
    def reverse: Repr
        //iterates through this seqence
        // and reverses 
        for (x <- this)
           //...etc


Then, what is interesting, is that the next two traits:

trait Seq[+A] extends SeqLike[A, Seq[A]]   //<-- NOTE here is the type for "Repr"
     //no mention of reverse

trait LinearSeq[+A] extends Seq[A] with SeqLike[A, Seq[A]] //<-- NOTE type again
    // no mention of reverse

both get a "free" version of the reverse function, but both of them work in terms of their respective "interfaces" (traits). So reverse on Seq returns a Seq.

Finally, the List concrete class implements the LinearSeq trait and redefines the reverse method (for efficiency purposes I assume).

sealed abstract class List[+A] extends LinearSeq[A]
                                 with LinearSeqOptimized[A, List[A]] 

      override def reverse: List[A] = {
          //faster impl without foreach iteration
          ...
      }

where the LinearSeqOptimized is simply another "chain" of traits that goes all the way up to SeqLike[A, Repr].

So you can see, how a generic "interface" was used to specify some "behavior", how an intermediate "interface" provided some kind of "default" implementation, and how all the sub-"interfaces" and subtypes all reaped the benefit of that implementation while preserving the return types to be of their "own" types. Phew. Confusing, I know!

like image 672
Andriy Drozdyuk Avatar asked Sep 28 '12 04:09

Andriy Drozdyuk


2 Answers

You can do it this way, although it can be a bit tricky.

Change the ReversableList interface to this:

public interface ReversableList<L, T> where L : ReversableList<L, T>
{
    // Return a copy of this list but reversed
    L Reverse();
}

Now you implement it like this:

public class ListImplementation<T> : ReversableList<ListImplementation<T>, T>
{
    public ListImplementation<T> Reverse() { /* code */ }
}
like image 59
Enigmativity Avatar answered Sep 27 '22 15:09

Enigmativity


How about something like

public interface IReversableList<T> where T : IReversableList<T>
{
    // Return a copy of this list but reversed 
    T Reverse();
}

public class ListImplementation<T> : IReversableList<ListImplementation<T>>
{
    public ListImplementation<T> Reverse()
    {
        return new ListImplementation<T>();
    }
}

Which can then be used as

var concreteList = new ListImplementation<string>();
ListImplementation<string> reversed = concreteList.Reverse();

EDIT

Added the constraint on the interface.

like image 45
Adriaan Stander Avatar answered Sep 27 '22 15:09

Adriaan Stander