/flags/csharp/dotnet-build-allowunsafeblocks
C# AllowUnsafeBlocks (-unsafe): fix CS0227 "Unsafe code may only appear if compiling with /unsafe"
- tool
- C#
- command
- dotnet build
- flag
- -p:AllowUnsafeBlocks=true (<AllowUnsafeBlocks> in the .csproj, csc -unsafe)
- default
- false: the unsafe keyword is an error
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> in the project file, or dotnet build -p:AllowUnsafeBlocks=true, lets C# code use the unsafe keyword: pointers, fixed statements, pointer arithmetic, stackalloc into a pointer and function pointers. Without it, any unsafe block, method or type fails with error CS0227: Unsafe code may only appear if compiling with /unsafe. The property maps to the compiler option -unsafe (/unsafe).
Without and with the property
dotnet new console -n App -o . >/dev/null 2>&1
cat > Program.cs <<'EOF'
int[] data = [10, 20, 30];
unsafe
{
fixed (int* p = data)
{
Console.WriteLine(*(p + 2));
}
}
EOF
build() { dotnet build --no-incremental "$@" 2>&1 | grep -oE "(warning|error) CS[0-9]+[^[]*" | sort -u; echo "exit: ${PIPESTATUS[0]}"; }
echo "== default"
build
echo "== -p:AllowUnsafeBlocks=true"
build -p:AllowUnsafeBlocks=true
dotnet run --no-buildOutput
== default error CS0227: Unsafe code may only appear if compiling with /unsafe exit: 1 == -p:AllowUnsafeBlocks=true exit: 0 30
The default build stops at CS0227. With the property set, the same code compiles and prints the third element, read through a pointer that fixed pins to the array.
What needs it
| Code | Needs AllowUnsafeBlocks |
|---|---|
| unsafe { ... } blocks, unsafe methods, unsafe struct/class | yes |
| Pointer types: int*, byte*, void* | yes |
| fixed (byte* p = array) and fixed-size buffers (fixed int buf[8]) | yes |
| int* p = stackalloc int[16] | yes |
| Function pointers: delegate* unmanaged<int, int> | yes |
| Span<int> s = stackalloc int[16] | no, safe code |
| Span<T>, MemoryMarshal, Unsafe class (System.Runtime.CompilerServices) | no |
| [DllImport] / [LibraryImport] with normal parameters | no, unless the signature uses pointers |
| [LibraryImport] source-generated marshalling | yes in many cases: the generated code uses pointers |
Setting it
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>Set it only in the projects that contain unsafe code, not repository-wide in Directory.Build.props, so a code review notices when unsafe code appears in a new project. For a quick experiment, dotnet build -p:AllowUnsafeBlocks=true works without editing the file (add --no-incremental if a previous build already succeeded with other settings).
Pitfalls
- Pointers into managed objects are only valid inside
fixed(or with a pinnedGCHandle); storing such a pointer and using it later reads memory the garbage collector may have moved. - The runtime does not check pointer arithmetic: reading past the end of the array is undefined behaviour, not an
IndexOutOfRangeException. - CS0214 (
Pointers and fixed size buffers may only be used in an unsafe context) is a different error: the project allows unsafe code, but the statement is outside anunsafeblock or method. - Language features also depend on the C# version: function pointers need C# 9 or later, see LangVersion.
FAQ
Does AllowUnsafeBlocks make my whole program unsafe?
No. It only allows the unsafe keyword. Code outside unsafe blocks, methods and types is compiled and verified exactly as before; only the marked regions may use pointers and other unverifiable constructs.
Do I need unsafe code for stackalloc or Span<T>?
No. Span<int> s = stackalloc int[16]; is safe code and needs no AllowUnsafeBlocks. You need unsafe only when you take raw pointers, for example int* p = stackalloc int[16]; or fixed (byte* p = buffer).
Is unsafe code faster?
Sometimes, in tight loops that avoid bounds checks, but Span<T>, Memory<T> and System.Runtime.InteropServices.MemoryMarshal usually reach the same speed without pointers. Measure before switching, and keep unsafe regions small.
Sources
- learn.microsoft.comlearn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/language#allowunsafeblocks
- learn.microsoft.comlearn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code
- learn.microsoft.comlearn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/fixed