In reality they have very different use cases.
ThreadStatic
can't transfer a value across anawait
or similar context switch.CallContext
can't retain a value per-thread.
So you see, one can not replace the other. ThreadStatic
is a low-level primitive. I don't think its use cases have gotten any fewer after CallContext
etc. came along. Note, its use cases are vanishingly small -- I think the last time I used it was probably over two years ago.
I would characterize things like Transaction.Current
as an abuse of TLS. It was never designed for this, and so when TLS appeared to break async, it was only because it never should have been used for that in the first place.