【问题标题】:Predefined macros for method names方法名称的预定义宏
【发布时间】:2012-10-21 00:50:58
【问题描述】:

在 C++ 中有预定义的宏,例如 __FUNCTION__,它编译为使用宏的函数名的字符串字面量。

void MyFunc()
{
    printf("I'm in %s!", __FUNCTION__); // I'm in MyFunc!
}

C# 有类似的东西吗?我希望为 asp.net 网络表单执行此操作:

public string MyProperty
{
    get { return (string)ViewState[__PROPERTY__]; }
    set { ViewState[__PROPERTY__] = value; }
}

显然这不起作用(否则我不会问这个问题),我想知道 C# 中是否有类似的东西不使用反射或与使用字符串文字 @987654324 相比有任何负面的性能影响@。

这有望减少我的拼写错误,但我可以想到其他一些有用的例子。

【问题讨论】:

  • 根据我使用 C# 的经验,您可以使用 #if debug #endif 等宏。但是,您不能拥有包含任何内容的编译时宏。
  • __FUNCTION__ 用于编译为字符串文字(我认为是在 gcc 2.96 中),但已经很多年没有了。变化有点痛苦。

标签: c# asp.net webforms c-preprocessor


【解决方案1】:

C# 的预处理器不支持具有关联值(如 C++)的宏,但您可以通过编译器生成的 Caller Information 使用支持 C# 5.0 及更高版本(因此至少是 VS2012+)的编译器来完成您想要做的事情。具体来说,通过 System.Runtime.CompilerServices 命名空间中的 CallerMemberNameAttribute。根据您的问题代码,我创建了以下示例来说明如何去做您想做的事情:

using System;

class ViewState
{
    public string this[string propertyName]
    {
        get { return propertyName; }
        set { }
    }
};
class View
{
    ViewState mState = new ViewState();

    static string GetCallerName(
        [System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
    {
        return memberName;
    }

    public string MyProperty
    {
        get { return (string)mState[GetCallerName()]; }
        set { mState[GetCallerName()] = value; }
    }
};
class Program
{
    static void Main(string[] args)
    {
        var view = new View();
        Console.WriteLine(view.MyProperty);

        Console.ReadKey();
    }
};

“MyProperty”将打印到控制台。编译时,编译器会将 GetCallerName 的 memberName 参数的默认值替换为调用构造的(属性、方法等)名称。所以程序员不需要维护代码

还应该注意的是,这还有一个额外的好处,就是能够很好地使用混淆工具,只要它们发生在编译后。

【讨论】:

    【解决方案2】:

    我不知道 ASP.NET WebForms 中是否有类似 ViewBag 的东西。万一没有,滚动你自己并不难。然后,您可以将 ViewState 包装在该类中,并根据需要获得常规属性成员访问权限。

    public class ExpandoViewState : DynamicObject
    {
        private readonly StateBag _viewState;
    
        public ExpandoViewState(StateBag viewState)
        {
            _viewState = viewState;
        }
    
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = _viewState[binder.Name];
            if (result != null)
                return true;
            return base.TryGetMember(binder, out result);
        }
    
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            _viewState[binder.Name] = value;
            return true;
        }
    }
    
    ...
    
    dynamic state = new ExpandoViewState(ViewState);
    var val = (string)state.MyProperty;
    state.MyProperty = "hi";
    

    【讨论】:

      【解决方案3】:

      您可以使用StackTrace and StackFrame 来获取当前方法的名称

      StackTrace st = new StackTrace(); 
      StackFrame sf = st.GetFrame(1);
      string method = sf.GetMethod().ToString();
      

      对于属性,返回的方法名称将包含 get_set_ 前缀。

      但是,我认为您不能像在 C++ 中那样真正将其重构为内联宏或函数。但是,如果您确实重构了一个实用程序方法来干燥它,您可能只需将 StackTrace 弹出一步来记录调用者的信息?

      【讨论】:

      • 感谢您的回答,但我正在寻找性能更高的东西,最好是编译时发生的东西。
      • @Matthew 它在编译时不会真正有用,因为您没有像在 C++ 中那样定义非文字值的编译时定义。您可以像输入此标识符一样轻松地输入实际的方法名称......也就是说,如果您是一个优秀的脚本编写者,您总是可以编写一个 Visual Studio 宏来用方法名称替换 __FUNCTION__
      • 可以在带有GetFrame(2) 的错误处理函数中使用它来获取调用者的姓名吗?这似乎更有用,并且会在仅在需要时发生的意义上执行!
      猜你喜欢
      • 2010-11-28
      • 2021-10-27
      • 2013-02-24
      • 1970-01-01
      • 1970-01-01
      • 2011-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多