You're wrong, and that's exactly what "unsafe" block does in C# - it does not affect the outer blocks. So, for example, if you wrap your pointer arithmetic inside "unsafe", you don't need the "unsafe" modifier on the method. If you put the modifier on the method, then you don't need the modifier on the class. And the code that uses your "unsafe" classes and methods doesn't need to be
in "unsafe" context.
If it were otherwise, no C# program would compile without "unsafe", because large parts of the standard library core (stuff in System) is unsafe. The only reason why "unsafe" is useful is precisely because it lets you isolate unsafe operations in a way that lets safe code invoke into them (assuming that you uphold your safety guarantees).
The /unsafe switch is a different thing - it needs to be turned out for the C# compiler to allow "unsafe" anywhere in its input, and is basically a project-level declaration that "this has some unsafe code somewhere in it".
"unsafe" is a C# feature. Verification is a CLR feature. And they are only related in a sense that things that you can do inside "unsafe" blogs generally produce unverifiable assemblies. But the reverse is not true, you don't have to use an "unsafe" block to consume an unverifiable assembly.
Nor does "unsafe" breaks verification by itself - it's only the specific features that you might use inside (like pointer arithmetic) that would do so, and even then not all of them. For example, C# requires "unsafe" for any use of sizeof() that is not known at compile-time, and for which it needs to emit the sizeof IL opcode. So e.g. sizeof(int) is okay, but sizeof(IntPtr) is unsafe; also, sizeof all structs are considered unsafe. However, the IL opcode for sizeof is not unverifiable, and so such an assembly would pass verification, despite requiring "unsafe" in C#.
Yes, but that's not what OP was talking about. Within an assembly, an unsafe block still doesn't taint any code that calls into it. And between assemblies, an unsafe assembly doesn't taint a safe one that depends on it. So it doesn't "propagate outwards".
If it were otherwise, no C# program would compile without "unsafe", because large parts of the standard library core (stuff in System) is unsafe. The only reason why "unsafe" is useful is precisely because it lets you isolate unsafe operations in a way that lets safe code invoke into them (assuming that you uphold your safety guarantees).
The /unsafe switch is a different thing - it needs to be turned out for the C# compiler to allow "unsafe" anywhere in its input, and is basically a project-level declaration that "this has some unsafe code somewhere in it".