Generic types API design

Coming from Java I am used to generics having type erasure at runtime and also wildcard types (like e.g. Foo<?>) at design time. In C# the type parameters are reified and take some getting used to. I am working on a framework where I have types using generics and also API methods which declare parameters of these generic types. But in many cases the methods don't really have to care about the type parameters, which is when I in Java would have declared the method parameter using a wildcard, rather than adding a type parameter to the method signature. Now I am wondering how this is typically approached in C#. What I ended up doing, but probably isn't what you are supposed to do, is that I declared an interface IFoo without type parameters and then another one with type parameters as IFoo<T> : IFoo. Now I can declare method parameters of type IFoo (or IFoo[] or similar) when I don't need to call methods which require the type parameters. I suspect that this is probably more of an anti pattern and I would like to learn how to better design APIs in C#.
4 Replies
boiled goose
boiled goose2mo ago
it's not uncommon to have a non generic interface with just object as a type as a root of a generic interface you can see this for example in IEnumerable, in List, and so on either that or you shuffle some types around changing the design
SleepWellPupper
SleepWellPupper2mo ago
The types you named exist due to legacy. I would disagree that it's common for newly developed libraries / framework components to implement this pattern. See e.g. the immutable or frozen types; they do not have nongeneric versions.
knut.wannheden
knut.wannheden2mo ago
In Roslyn I did find some types where there are both generic and non-generic versions (e.g. CSharpSyntaxVisitor), but they don't inherit from each other, which I suspect is weird. I suppose I could always add another interface, which they both extend.
boiled goose
boiled goose2mo ago
they are legacy types but i don't think there is any other way to "bypass" the generic constraint except again changing the design/inheritance