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!
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!
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 */ }
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With