I got this great question from the mailbag:
    "[W]hat is the relation between the "# line hidden" directive and the DebuggerNonUserCode attribute ? Are these the same ?"

Short Answer:
They're both markings that library authors can put in their source code to mark certain portions as "hidden" to the end-user while debugging. In both cases, the debugger must detect them and do additional work to respect them. Visual Studio respects both of these. MDbg only respects #line hidden.  #line marks lines within a function as hidden whereas the DebuggerNonUserCode marks entire functions as hidden.

More comparisons:

  #line hidden DebuggerNonUserCode 
What does it affect? single set of continuous lines within a function entire function or class
What does compiler do with it? (How could other languages besides C# use this) Changes PDB mappings. Instructs the line to be mapped to 0xFeeFee, which serves as special value to debuggers to instruct it that that line is hidden. Just a normal custom attribute, stored in the executable file.
More info on MSDN : wiki link wiki link
Example usage and more detail from my blog here here
Dependency on CLR None.  Existed since  .NET 1.0. Depends on Just-My-Code (JMC), which was added in .Net 2.0
What could debugger do with it? This is purely a contract between the compiler and the debugger communicated via the PDBs.
If debugger stops at a line that maps to '#line hidden' it can elect to keep executing. It may elect to forbid breakpoints to be placed at hidden points. It stepping would normally stop at a hidden line, it may choose to continue stepping.
This requires the debugger to use JMC support from the CLR debugging services.
Debugger must use Just-My-Code stepping and mark functions as non-user code. Debugger marks frames with the DebugNonUserCode attribute as non-user code.

Note that since these are both enforced by the debugger, the debugger has to do extra work to make these features available to the end-user. But this also lets the debugger decide exactly what sort of policy it wants to expose. For example, the debugger can decide how to handle a breakpoint set in a function marked as DebuggerNonUserCode. (eg, Forbid it, warn the user, stop anyways, etc)