The issue is that inside of the % { } block $_ does not contain the value of column b but rather an object with a b property. When you run [string]::IsNullOrEmpty($_) you are testing if this object is null or an empty string, neither of which is true.
To illustrate what's happening, compare the results of...
Import-Csv .\test.csv | Get-Member
...with the results of...
Import-Csv .\test.csv | select b | Get-Member
...with the results of...
Import-Csv .\test.csv | select -ExpandProperty b | Get-Member
One solution is to test if the b property of the object produced by select (rather than the object itself) is null or empty:
import-csv test.csv | select b | % {
Write-Host "$_ $([string]::IsNullOrEmpty($_.b))"
}
Another solution is to pass only the value of column b to the % { } block:
import-csv test.csv | select -ExpandProperty b | % {
Write-Host "$_ $([string]::IsNullOrEmpty($_))"
}