Fun with CallerLineNumberAttribute: clever hack or terrible idea? One fun feature that was added (fairly) recently in .NET is the [CallerLineNumber] attribute, which, in conjunction with [CallerFilePath] and [CallerMemberName] can allow for some very nice logging methods.

I was thinking today, though, that they might be used for another interesting purpose.  Consider the case where you have some event that should trigger on a Boolean transition.  The classic implementation for this uses a field to keep track of the 'last state', then updates the field after performing the check.  The annoying thing, though, is that you need a flag for every case and each flag needs a name.  So I thought of something like this:

public sealed class IndexedBuilder&lt;T&gt;
    where T : class, new()
{
    private readonly Dictionary&lt;int, T&gt; _instances = new Dictionary&lt;int, T&gt;();

    public T Build(int index)
    {
        T instance;

        lock (_instances)
        {
            if (!_instances.TryGetValue(index, out instance))
            {
                instance = new T();
                _instances.Add(index, instance);
            }
        }

        return instance;
    }
}

public sealed class TransitionDetector
{
    private readonly IndexedBuilder&lt;Context&gt; _builder = new IndexedBuilder&lt;Context&gt;();

    public bool DetectUp(bool state, [CallerLineNumber] int lineNumber = 0)
    {
        return _builder.Build(lineNumber).DetectUp(state);
    }

    public bool DetectDown(bool state, [CallerLineNumber] int lineNumber = 0)
    {
        return _builder.Build(lineNumber).DetectDown(state);
    }

    private sealed class Context
    {
        private bool? _lastState;

        public bool DetectUp(bool state)
        {
            bool result = state &amp;&amp; (_lastState == false);
            _lastState = state;
            return result;
        }

        public bool DetectDown(bool state)
        {
            bool result = !state &amp;&amp; (_lastState == true);
            _lastState = state;
            return result;
        }
    }
}

public static class Program
{
    public static void Main()
    {
        var detector = new TransitionDetector();

        int x = 0;
        int y = 0;
        while (x &lt; 10000)
        {
            if (detector.DetectUp(y &gt; 100))
                Console.WriteLine("y &gt; 100");

            if (detector.DetectDown(x &lt; 500))
                Console.WriteLine("x &gt;= 500");

            y++;
            x++;

            if (y &gt; 1000)
                y = 0;
        }

        Console.ReadLine();
    }
}


This is a specific example of the more general pattern of allowing an instance to be bound to a particular location in the code, without explicitly naming or referencing the instance.  The biggest issue I see is that this breaks down if you attempt to perform multiple calls on the same line in the code or if you pass the TransitionDetector instance such that it gets called from another file, which means documentation required and there is a significant potential for incorrect use.
This is definitely an interesting use of [CallerLineNumber] but I think that the risk for people using it wrong resulting in hard to find bugs is to great. If you take an example from the issue you yourself mentioned.

if (detector.DetectUp(y &gt; 100) ||
    detector.DetectUp(x &lt; 500))
{...}


Would not as one might think have the same behavior as

if (detector.DetectUp(y &gt; 100) || detector.DetectUp(x &lt; 500))
{...}


Another gotcha I found was that lambda functions does not give the line or name of where they are called but where they are defined. This makes sense but might not be what the user expects. The example below prints Main: 10 and not HelpFun: 16.

using System;
using System.Runtime.CompilerServices;

namespace CallerLineNumberTest
{
    class Program
    {
        static void Main(string[] args)
        {
            HelpFun(() =&gt; PrintLineNr());
            Console.ReadKey();
        }

        private static void HelpFun(Action a)
        {
            a();
        }

        private static void PrintLineNr([CallerMemberName] string name = "",
            [CallerLineNumber] int lineNumber = 0)
        {
            Console.WriteLine(name + ": " + lineNumber);
        }
    }
}