Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic class with an interface constraint vs class implementing interface

Recently I was implementing a Trie data structure and decided the Nodes could store different types of data or have its implementation varied so then I went for Node<T>. Then as I got into the algorithm for constructing the Trie I realised it required more intimate knowledge of the Node so I constrained the generic class to use an INode interface. This allows for more flexibility but felt wrong in the context of a generic class.

Generic classes have a different use case to classes which implement an interface. For example, List<T> - the algorithm can work without being dependent on a related set of abstractions. A class which implements an interface may require polymorphism/DI but the interfaces will be more specialized.

Under what circumstances do others apply a generic class T where T may implement a more specialized interface?

I thought that a generic class is used when T does not really need to expose operations/data though I can see a generic class may be used where T implements IDisposable or some other more general interface.

Any help in clarifying these points?

like image 708
Jaycee Avatar asked May 23 '26 09:05

Jaycee


1 Answers

When faced with a choice to use a generic with an interface constraint vs. a non-generic with an interface type, I would go for generic+interface only in situations when some or all of types passed as generic arguments are value types. This would prevent my implementation from requiring costly boxing and unboxing when dealing with my structs.

For example, if the interface happens to be IComparable, I wold definitely prefer a generic with a constraint, because it would let me avoid boxing when working with primitives.

Note that an alternative way of providing functionality to your generic class is passing a delegate along with the value. For example, if you plan to do something like this

interface IScoreable {
    decimal GetScore(object context);
}
class Node<T> where T : IScoreable {
    ...
    void DoSomething(T data) {
        var score = data.GetScore(someContext);
        ...
    }
}

you can also do this:

class Node<T> {
    private Func<T,object,decimal> scorer;
    public Node(Func<T,object,decimal> scorer) {
        this.scorer = scorer;
    }
    ...
    void DoSomething(T data) {
        var score = scorer(data, someContext);
        ...
    }
}

The second solution lets you "decouple" the scoring functionality from the type being scored, at the expense of having the caller to write a little more code.

like image 77
Sergey Kalinichenko Avatar answered May 25 '26 22:05

Sergey Kalinichenko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!