0

Consider this code fragment:

    public void Run()
        {
        var caseInsensitiveParser = new Parser(with =>
            {
            with.CaseSensitive = false;
            with.IgnoreUnknownArguments = true;
            with.HelpWriter = Console.Error;
            });
        try
            {
            options = caseInsensitiveParser.ParseArguments<HorizonAppOptions>(commandLineArguments);
            if (options.Errors.Any()) { errorMessages.Add("Unable to process command line arguments."); }
            }
        catch (Exception ex)
            {
            errorMessages.Add(ex.Message);
            }

        IHorizonExporter exporter = null;
        IHorizonImporter importer = null;

        try
            {
            if (!string.IsNullOrWhiteSpace(options.Value.Importer))
                {
                importer = GetImporter();
                importer.ProcessCommandLineArguments(caseInsensitiveParser, commandLineArguments);
                }
            }
        catch (Exception ex)
            {
            errorMessages.Add(ex.Message);
            }

        try
            {
            if (!string.IsNullOrWhiteSpace(options.Value.Exporter))
                {
                exporter = GetExporter();
                exporter.ProcessCommandLineArguments(caseInsensitiveParser, commandLineArguments);
                }
            }
        catch (Exception ex)
            {
            errorMessages.Add(ex.Message);
            }

        if (importer==null || exporter==null)
            errorMessages.Add("Both an importer and an exporter must be specified.");

        if (errorMessages.Any())
            {
            foreach (var errorMessage in errorMessages) { Console.WriteLine(errorMessage); }
            DisplayImportersAndExporters();
            Environment.Exit(-1);
            }
        else
            {
            // Perform the import and the export.
            var horizon = importer.ImportHorizon();
            exporter.ExportHorizon(horizon);
            }
        }

I'm hitting the second catch() clause and getting some very odd results. The exception originates from an attempt to open a file that doesn't exist. I expect to get a DirectoryNotFoundException, but inside the catch block I see that ex is actually null, as you can see from the data tip: Null exception? Huh? How can the caught exception be null? Surely that's an oxymoron, if I'm inside a catch block, the exception can't be null?

Now wait a minute... In the debugger's Locals window, there's this: enter image description here

What?? How can there be three variables with the same name all in scope?

Am I losing my sanity? I'm positive this is a pattern I've used for many years without problems...

UPDATE

Yuriy Faktorovich suggested in the comments that .NET exception caught is unexpectedly null might be relevant. I think he is on to something, as I am using Code Contracts in the solution but not in this particular method. It does seem like Code Contracts are implicated though. One of the responders to that other question suggested that renaming the variables worked and, indeed it does. If I call them ex1, ex2 and ex3 then all is well. I think there is a subtle difference in the IL when CCREWRITE has been used and the debugger is getting confused.

Community
  • 1
  • 1
Tim Long
  • 13,508
  • 19
  • 79
  • 147
  • 1
    I doubt there is a scoping issue, more likely to be an issue with the debugger I think. – clean_coding Jan 10 '16 at 17:26
  • Unlikely, but is it possible http://stackoverflow.com/questions/5650868/net-exception-caught-is-unexpectedly-null is your problem? I'd also definitely make sure your debugging symbols match your compiled code. – Yuriy Faktorovich Jan 10 '16 at 17:31
  • @YuriyFaktorovich I would say you could well be on to something there as I am definitely using Code Contracts in this project. I will have a look at the IL. – Tim Long Jan 10 '16 at 17:34
  • You are just discovering a CLR implementation detail, it does not in fact supports limiting the scope of a local variable. They are all global to the method body, as you can tell from the IL. Being able to re-use the variable name is a C# feature, not a CLR feature. It does not cause problems at runtime, the jitter still generates metadata that describes where they are relevant so the GC can do an optimal job. A feature that is disabled [when you debug](http://stackoverflow.com/a/17131389/17034). – Hans Passant Jan 10 '16 at 18:15
  • @YuriyFaktorovich please see my update in the question. Code Contracts do seem to be implicated because of some subtlety of CCREWRITE. For now, renaming the variables fixes everything and the debugger is happy. – Tim Long Jan 10 '16 at 18:41
  • @HansPassant I came to a similar conclusion and in fact I've deleted that content from the question so your comment probably won't make any sense now - sorry ;-) Code Contracts does seem to be implicated somehow but I'm not able to explain it based on my brief look at the IL. I think dotPeek might have been confusing me a bit too, as when I used ILDASM I couldn't really spot any difference with and without CCREWRITE being used. – Tim Long Jan 10 '16 at 18:44

0 Answers0