The lack of erasure means that you can use generics to do sophisticated type magic, sort of like in C++ templates - specifically using reflection. You don't usually want to do this too much, but doing it in one or two places can simplify code tremendously. This also allows you to eliminate boxing and reflection overhead in a lot of places by letting you use generics to bind to reflected types/members at runtime, which is really important if you're trying to build a performant app in C# that's got extensibility support.
For example, I use this to create strongly-typed bindings to individual fields/properties of class instances at runtime, which lets me pass around objects of types like 'BoundMember<int>', and generically store into them - essentially the same as 'ref int', except it can outlive function scope. This is really useful when doing asynchronous programming, and it lets you kill duplication when building things like configuration dialogs.
Hmm, sounds like what I said with "unless you're doing some crazy reflection stuff ", and it sounds like a solution that reduces the readability and maintainability. But maybe I'd need to see a good example to appreciate it.
For example, I use this to create strongly-typed bindings to individual fields/properties of class instances at runtime, which lets me pass around objects of types like 'BoundMember<int>', and generically store into them - essentially the same as 'ref int', except it can outlive function scope. This is really useful when doing asynchronous programming, and it lets you kill duplication when building things like configuration dialogs.