This is the "expression problem" https://en.wikipedia.org/wiki/Expression_problem
In FP with ADTs, anyone can write new functions for a datatype. Yet, as you say, adding a new case requires modifying the definition and existing functions.
In OOP, anyone can add a new case, by defining a new subclass. Yet writing a new method for that class requires modifying the definition and existing subclasses.
One of these isn't always better than the other, so it's useful to have both.
> If you try to describe this properly in C# you'll be writing an abstract outer class and N concrete inner classes, plus boilerplate for comparisons and so on.
This overhead is due to emulating one approach using another. It would also take a bunch of boilerplate to implement subclassing with ADTs (e.g. a sum type to describe the methods, a smart constructor to implement the inheritance, etc.).
(Personally I prefer ADTs too, but they're not a magic bullet)