Partial Classes and future-proof vindication

A common question is "How can my tool crack the PDB to automatically determine what source file / line number a .NET class is defined in?" 

It's a trick question.

There's a problem with the question. A .NET class may not be defined at a single source file / line number.

How should you specify what source file and line number a .NET class is defined in?  Ok, that's easy for C# 1.0 since the entire class is contained in a single source file, and you could even say the line number is where the 'class' keyword appears.

In that case, you could use a C# 1.0 specific technique (such as grepping source files). But managed PDBs are targeted more generally at the IL level, and so the design tried to avoid getting tied to specific C# or other language semantics.

Managed PDBs provide source mappings for executable IL opcodes within a function body. That has well defined semantics. An executable opcode can map to an instruction address, which can map to a source line. But mapping .NET class declarations to line numbers as used generally across different .NET languages is not so clear...

When people asked this back in the C# 1.0 days, we'd emphasize the issue at the IL level. But since it wasn't a problem in C# 1.0, there wasn't a killer counter-example. All of obvious examples made it appear that classes had a well defined file/line number.


Corner cases and Future-proofing

We'd suggest that they could approximate the answer by just using the source mapping for an arbitrary method in the class. Of course, this wasn't 100%.

Maybe the class was empty or didn't have any method bodies. Maybe the class was produced from some less common codegen construct, in which case people didn't want to run the query anyways. Maybe the functions were defined across multiple files.   You could even force that with the #line directive. But folks asking usually considered these to be insignificant corner cases. There were certainly a lot of corner cases, but unless you really understood the problem space, it was never really convincing; the corner cases just seemed like excuses. ("oh, we don't need to handle such an obscure case. We just need a solution that works for the mainline cases") 

One could also view our obstinance of adhering to the protocol as defending the invariants and future-proofing.

The vindication

C# 2.0 introduced Partial Classes, which provided 1st-class mainline language support for splitting a class across multiple source locations. And then the default designers leveraged this so that you could write your user code in foo.cs and auto-generated code for your class would go in a file like foo.designer.cs. So now there was a mainstream common case that showing that a class may not be defined at a single source file / line number. 

Comments (0)

Skip to main content