help with generic interface
im writing a small binary tree library for fun and ran into a small problem
I made a small interface for Node and Tree:
however the Node interface can only reference itself if that makes sense?
public interface Node<T> where T : IComparable<T>
43 Replies
the issue here is that I want SplayTreeNode.Getleft to return a SplayTreeNode
but if I make another Node like an AVLTreeNode ill want it to return an AVLTreeNode
ill then maybe use the fact that they are both of type Node and put them in a Node<T> but I want my function signature to be stricter
how can I do that?
So you would want say a
AVLTreeNode<T> : SplayTreeNode<T>
to override the return type with itself?
Or if all the nodes are to implement INode without inheriting between themselves, you can easily do that by exposing custom implementations using the respective return types while explicitely implementing INode to return INodethey can't override eachother I think
so AVLTreeNode can't also be a SplayTreeNode but both of them are ITreeNodes
I ended up writing some abomination that works I just think its deep in an antipattern
the problem is that im also doing another layer over this
Having the node type as a genric has advantages like writing utility methods that work with any node and and being able to return the right type
and I end up with these types:
I would however add a less specific base interface where left and right are INode
By having the node type as a generic, you are forced to know it which may not always be possible. Having the second layer allows for more type safety when the node type is known while keeping the broader abstraction for when it's not
wouldn't it still work?
if I have like
Being a base interface, the declaring type would be ITreeNode but the runtime type would be the specific class
ah so I can still have the behavior when I do it, but not a guerentee that all the values of ITreeNode are the same class?
You can reference it through the base interface and get ITreeNodes from it yes. GetLeft and GetRight would just be implemented by calling the respective implementations returning the Self type
ah I see ty
I think it should work
its just super ugly...
You can do that by providing default implementations for the base interface in the Self interface
yea, the reason I want the interface is for some shared behavior
Generics can get ugly :dviperShrug: just depends on your purposes. You can overengineer it for max functionality or just don't if it's not needed
I just didn't send it here because I didn't want to write huge blobs of text 🙂
im going to write a few trees so im better off with the big type signtures I think
thanks a lot 🙂
(look at the declarations of numeric types for ugly but max functionality :NotLikeThis: )
😱
pain
It just makes sense to maximize functionality if it's to be used by millions of devs
yea
One last thing if your nodes are to be immutable at certain levels, you should consider making these layers covariant.
huh?
what do you mean?
Covariance and Contravariance (C#) - C#
Learn about covariance and contravariance and how they affect assignment compatibility. See a code example that demonstrates the differences between them.
oh, so if I can't change a leaf for example
ITreeNode<object> = new AVLNode<Foo>()
would be supported
As long as ITreeNode is immutablehmm
I don't think I can guerentee that
Then covariance doesn't make sense in this scenario and shouldn't be forced
as all trees have pointers to their children and an insert will change them
yea, in rust I think I had the same idea, just didn't do it because of that
ownership of data is hard in these structures
But an example is IEnumerable<T>. Since those are immutable, T is marked as covariant and you can do
IEnumerable<object> = new Foo[10]
ah yea
so you can quicly cast from one to another
maybe I can do some covariants but I don't think I will do em
only me writing the code lol
Not quite cast as Foo to object is implicit. Just smarter type-safety
I see
The same way you can do
object = Foo
but wrapped in the context of a read-only genericone last question if you don't mind
sure
in my Itreenode I implemented a generic search min/max functions:
when I write my tree it still asks me to implement them
why is that? can it not just use the ones I wrote for the interface?
In what context are you calling them?
Default implementations are only visible when accessed through the interface and aren't implemented to be visible in the type
Also may not be related but in the while loops you're accessing GetLeft/Right without calling 🙂
I have the Node implementing the TreeNode as normal without the GetMin/GetMax:
and then when I try to use it
I get
oh oops ty 🙂
As mentionned, you won't be able to call the default implemented methods from the class. They are only visible when accessing through the interface
so I have to cast it first?
That's one approach but the class should also implement it directly so it's exposed without having to cast everywhere
ah I should cast once, inside my SimpleTreeNode class
and have then there is no need to cast outside it
Just keep in mind boxing if implementing the interface in a struct
I see, thenks a lot, ill get to implementing some trees then 🙂
oh I can just add the accessibility thing to my interface
and then I can acceess it
is internal for optimization or visibility?
ah now I think I get it
actually no its not working how I think it works lol
it looks neat, but I think ill stick to the explicit definitions everywhere as im only going to implement like 5-6 trees I think