You're considering the "call into C" case, which is only half the story. "C calls into you" will always be easier in C++ and Rust, or any other language with "no" runtime, than heavier languages that rely on them. Yes, it can work, but it's a disadvantage.
In C# I use callbacks where I find appropriate (logging, also some rare events). Under the hood, the runtime marshals delegates (most languages would call them lambdas) into unmanaged C function pointers.
Ease of use is the same when I consuming the C API from e.g. C++. No disadvantage for C#.
One neat trick with .NET on Windows, is that you can actually export static methods in assemblies as unmanaged entry points. In other words, things can LoadLibrary/GetProcAddress them, and invoke them as native.
C# doesn't support this out of the box, but it can be easily done by post-processing the generated assembly. There's a NuGet package for that.