Odd string interpolation Null Reference Exception when debugging
Asked Answered
B

4

7

UPDATE TO THE UPDATE (Because this is just getting lost in bizarro world)

OK, I'm now totally blow away. I noticed in my update, I was missing the period after {b} on line 7. So, being a sticker for accuracy, I added it in.

!!!Now Line 7 also fails when you step into it!!!

What in the world????

So, now Program.cs with more crazy results...

try
{
    var a = "a";
    var b = "b";
    var r1 = "r";
    var r1x = $"r2x1={a}.{b}.{r1}";  // < Line 6 - F10 here will give you an exception
    var r1y = $"r2x1={a}.{b}" + r1;  // < Line 7 - F10 here will execute just fine!
    var r1z = $"r2x1={a}.{b}." + r1; // < Line 8 - F10 here will give you the exception!!!
    var r1a = $"r2x1={a}.{b}{r1}";   // < Line 9 - F10 here will give you an exception
    Console.WriteLine(r1x);
    
}
catch (NullReferenceException e)
{
    Console.WriteLine(e);
}

Update

I messed around some more with this, and the issue seems to be directly related to the string interpolation. In the updated Program.cs directly below, adding a breakpoint on Line 6 (var r1x = ...) will create an exception if you F10 over it, will happily run if you F5 it. However also having a breakpoint on line 7 (var r1y = ...) will happily allow you to F10 past it!

try
{
    var a = "a";
    var b = "b";
    var r1 = "r";
    var r1x = $"r2x1={a}.{b}.{r1}"; // < Line 6 - F10 here will give you an exception
    var r1y = $"r2x1={a}.{b}" + r1; // < Line 7 - F10 here will execute just fine!
    Console.WriteLine(r1x);
    
}
catch (NullReferenceException e)
{
    Console.WriteLine(e);
    
}

Original question below

Program.cs:

try
{
    var a = "a";
    var b = "b";
    var r1 = "r";
    var r2 = "r";
    r1 = $"r1={a}.{b}.{r1}";
    r2 = $"r2={a}.{b}.{r2}";
    Console.WriteLine(r1);
    Console.WriteLine(r2);

}
catch (NullReferenceException e)
{
    Console.WriteLine(e.StackTrace);
}

This silly bit of code will run just fine - if you're not doing line by line step-through with the debugger.

However placing a breakpoint on line 8 (r2 = ... ) will drop you into the catch with a NullReferenceException and

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Buffer.Memmove(Byte& dest, Byte& src, UIntPtr len)
   at System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendStringDirect(String value)
   at Program.<Main>$(String[] args) in /Users/rambler/tmp/Four Weeks/why/ConsoleApp1/Program.cs:line 8

Equally doing a breakpoint and line 7 will yield the same exception. Not having a breakpoint on either line will however result in an exception free execution.

I've tried several variations of the r1 = ... and r2 = ..., some which use string interpolation with r1 (or r2 respectively) and I get hard to pin down results.

All times though the code runs if I don't try to debug that particular line.

So far this has been on my Mac using both Rider and Visual Studio Code. Going to fire up Windows later on to see if I can replicate it there as well.

This issue came to light on a much much bigger app I'm writing - and I've been finally able to limit it down to just this.

.net 7.0.203

Brownlee answered 16/4, 2023 at 15:40 Comment(7)
Have you tried concatenation instead of interpolation ?Emmy
Yeah, I mentioned that in the question. It's hard to define because what works one time, seemingly fails the next.Brownlee
Can you add a var temp_r2=r2; just before the r2= and then replace that r2= line with r2 = $"r2={a}.{b}.{temp_r2}";Browder
Still get the exception!Brownlee
The following post regarding string interpolation contains some information that may be of interest.Pylon
Thanks, but I'm not seeing how, especially as my question is specifically about the exception being raised ONLY if one steps past the line with the debugger. Which part of the post do you feel is pertinent to the above issue?Brownlee
They're bread crumbs that you may or may not choose to follow. If one wishes to further investigate the issue, one will likely need to have a thorough understanding of how string interpolation works.Pylon
B
2

This has been confirmed by others:

https://github.com/dotnet/runtime/issues/78991

Brownlee answered 19/4, 2023 at 15:16 Comment(1)
They've 'closed' the ticket, yet it is still a problem in .net 8.Huey
H
1

A simple hack is to turn off optimizations on the method until you have debugged and unit tested it thoroughly. Just add this to the method declaration...

[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]

No need to change your code. Remove the attributes when you are done or when it is actually fixed, your choice.

I debugged it and it has to do with optimizations in Buffer.cs where it is trying to use Memmove into an uninitialized buffer destination which occurs when you only have a single character ahead of an interpolated string.

[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]        
public static string MyMethod(string parameter)
    {
       //your interpolated code here will debug now
        try
        {
            var a = "a";
            var b = "b";
            var r1 = "r";
            var r2 = "r";
            r1 = $"r1={a}.{b}.{r1}";//set break-point here and step over
            r2 = $"r2={a}.{b}.{r2}";//set break-point here and step over
            Console.WriteLine(r1);
            Console.WriteLine(r2);

        }
        catch (NullReferenceException e)
        {
            Console.WriteLine(e.StackTrace);
        }
       // etc.
    }

Please note that the problem occurs when inside a try block. Without the NoOptimization and NoInlining attribute options the above code results in the following exception (as of .net 8)

An unhandled exception of type 'System.NullReferenceException' occurred in System.Private. CoreLib.dll: 'Object reference not set to an instance of an object.'
at System. Buffer Memmove(Byte& dest, Byte& src, UIntPtr len) in / /src/libraries/System.Private.CoreLib/src/System/Buffer.cs:line 236
at System.Runtime. CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(String value)
in 137
Huey answered 7/9 at 0:48 Comment(0)
Y
0

Computed properties/values are a challenge for a debugger, for the debugger is asking a variable to compute and show a value in a time/state that the value is not meant to be accessed, or it's in the middle of a complex operation and that is a failure caught by the debugger.

The debugger is in a type of race condition with the interpolation since the code works just fine in an application operation; there is some point where a null reference is being written or overwritten and being propagated by the debugger.

Hence one could say there is an issue with the debugger, but one codes for a real-world scenario and not a debugging scenario.


I see this all the time with classes that have properties which compute something. In the debugger it shows an exception for a property that is only seen during debugging.

Yolandoyolane answered 16/4, 2023 at 17:30 Comment(1)
I'm fully willing accept this is a bug, but the debugger should never fight you. Especially on such a simple single threaded example as this. And doubly so given how it handles lines 7 and 8.Brownlee
W
0

I ran into this the other day. Reading through the github issue, it looks like a fix for this is targeted for .NET 9; in the meantime here is a summary of workarounds discussed:

  • Add "DOTNET_EnableAVX": "0" to environment variables in debug launch configuration (this worked for me)
  • Add .ToString() to the expressions inside the string interpolation; e.g. $"Some format string {obj1.ToString()}, {obj2.ToString()}"
  • Use string.Format("Some format string {0}, {1}", obj1, obj2) instead of string interpolation
  • Use plain string concatenation; i.e. "Some format string " + obj1.ToString() + " " + obj2.ToString()
Wilsey answered 25/7 at 21:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.