Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get property name when it uses yield return

How do i get property name of the executing property. If the property uses "return" then MethodBase.GetCurrentMethod().Name returns the name of the property. But when I use "yield return" MethodBase.GetCurrentMethod().Name returns "MoveNext". how do I get the executing property name when it uses yield return?

Sample Code

    class Program
    {
       static void Main(string[] args)
       {

         var x = myProgram.Something;

         Console.ReadLine();
       }       
   }

public class myProgram
{
    public static IEnumerable<string> Something
    {
        get
        {
            string var = MethodBase.GetCurrentMethod().Name;
            for (int i = 0; i < 5; i++)
            {
                yield return var;
            }
        }
    }
}
like image 720
kumar Avatar asked Mar 04 '12 02:03

kumar


2 Answers

As you've probably noticed, the compiler reorganizes how the methods work and Something returns a private class that implements IEnumerable. As such, the actual contents of your method appear in the MoveNext method of this private class, so MethodBase.GetCurrentMethod doesn't return what it seems like it should return.

It happens that the name of the private class is derived from the original method name, which in this case is <Enumerate>d__0. So, you can parse the original method name from a stack frame.

static IEnumerable<string> Enumerate()
{
    var method = new StackTrace(true).GetFrame(0).GetMethod().DeclaringType.Name;
    yield return Regex.Replace(method, @".*<([^)]+)>.*", "$1");
} 

static void Main(string[] args)
{
    foreach (var @string in Enumerate())
    {
        Console.WriteLine(@string);
    }
}

This is, of course, a hack and could easily not work in future versions of .NET

like image 69
kelloti Avatar answered Oct 09 '22 19:10

kelloti


As you can probably guess, the problem here is that a yield return statement does a bit of rewriting behind the scenes, similar to how a using or lambda expression does. It actually gets implemented as an enumerator, with the code that calls yield return being part of the MoveNext method on the enumerator.

This is an overall problem of using Reflection: it gives you runtime information about your executing code, which may not match your compile-time idea of what that code was.

That's a long-winded way of saying that there's no easy way to get the information you want. If you were to move the yield return next into a separate method, then any code outside of that method would not be part of the MoveNext, but that may or may not accomplish what you need. You are no longer actually getting the name of the method that is executing the yield return, you are getting the name of it's caller. If that's all you care about, it looks like this:

public IEnumerable<string> Something
{
    get
    {
        var x = MethodBase.GetCurrentMethod().Name;
        return this.DoSomething(x);
    }
}

private IEnumerable<string> DoSomething(string x)
{
    for (int i = 0; i < 5; i++)
    {
        yield return x;
    }
}

EDIT: While I doubt it will help you in the short term, for the record, this problem is also solved when using C# 5's new attributes. Since the CallerMemberName attribute is resolved at compile time, and apparently before the iterator has been rewritten into an enumerator class, it produces the name of the property:

public IEnumerable<string> Something
{
    get
    {
        var x = this.GetCallerName();
        for (int i = 0; i < 5; i++)
        {
            yield return x;
        }
    }
}

private string GetCallerName([CallerMemberName] string caller = null)
{
    return caller;
}
like image 43
Michael Edenfield Avatar answered Oct 09 '22 20:10

Michael Edenfield