【问题标题】:How to get property name when it uses yield return使用收益返回时如何获取属性名称
【发布时间】:2012-03-04 02:26:02
【问题描述】:

如何获取执行属性的属性名称。如果属性使用“return”,那么 MethodBase.GetCurrentMethod().Name 返回属性的名称。但是当我使用“yield return”时 MethodBase.GetCurrentMethod().Name 返回“MoveNext”。使用 yield return 时如何获取执行属性名称?

示例代码

    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;
            }
        }
    }
}

【问题讨论】:

  • 用例是什么?如果我们了解需求和背景,我们可能会为您提供更好的帮助。
  • 你想达到什么目的?
  • @Jason,我可以想到几个用例——其中一个就是日志记录。这是个好问题

标签: c# properties yield-return


【解决方案1】:

您可能已经注意到,编译器重新组织了方法的工作方式,Something 返回一个实现IEnumerable 的私有类。因此,您的方法的实际内容出现在此私有类的MoveNext 方法中,因此MethodBase.GetCurrentMethod 不会返回看起来应该返回的内容。

私有类的名称恰好是从原始方法名称派生的,在本例中为&lt;Enumerate&gt;d__0。因此,您可以从堆栈帧中解析原始方法名称。

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);
    }
}

当然,这是一种 hack,在未来的 .NET 版本中很容易无法使用

【讨论】:

  • 名称并没有真正混淆。混淆是故意试图隐藏代码的作用,而这里不会发生这种情况。像这样的名称被称为“不可言说”,因为它们不是有效的 C# 名称。另外,这里不需要StackTraceMethodBase.GetCurrentMethod()就足够了。
  • 感谢帮助我现在知道它是一个 hack。
【解决方案2】:

正如您可能猜到的,这里的问题是 yield return 语句在后台进行了一些重写,类似于 using 或 lambda 表达式所做的事情。它实际上是作为枚举器实现的,调用yield return 的代码是枚举器上MoveNext 方法的一部分。

这是使用反射的一个整体问题:它为您提供了关于您正在执行的代码的运行时信息,这可能与您对代码的编译时概念不匹配.

这是一种冗长的说法,即没有简单的方法可以获取您想要的信息。如果您要将yield return next 移动到一个单独的方法中,那么该方法之外的任何代码都不会成为MoveNext 的一部分,但这可能会也可能不会满足您的需求。您实际上不再获得正在执行yield return 的方法的名称,而是获得了它的调用者 的名称。如果这就是你所关心的,它看起来像这样:

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;
    }
}

编辑:虽然我怀疑它会在短期内对您有所帮助,但作为记录,使用 C# 5 的新属性时也解决了这个问题。由于CallerMemberName 属性是在编译时解析的,并且显然在迭代器被重写为枚举器类之前,它会产生属性的名称:

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;
}

【讨论】:

  • 感谢您提供的信息,我们将密切关注 CallerMemberName。由于其他限制,我将使用 StackTrace 解决方案。
【解决方案3】:

我会将迭代器移动到辅助方法中:

public class myProgram
{
    public static IEnumerable<string> Something
    {
        get
        {
            string var = MethodBase.GetCurrentMethod().Name;
            return GetSomethingInternal();
        }
    }

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-27
    • 1970-01-01
    • 2020-04-21
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多