Predefined macros for method names

2019-06-27 12:28发布

In C++ there are predefined macros such as __FUNCTION__, which compile to a string literal of the function name the macro is used in.

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

Is there anything similar for C#? I am looking to do this for asp.net web forms:

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

Obviously this doesn't work (otherwise I wouldn't ask the question), I would like to know if there's something similar in C# that doesn't use reflection or have any negative performance impacts versus using a string literal "MyProperty".

This will hopefully cut down on typos on my end, but I can think of a few other instances where this would be useful.

3条回答
Root(大扎)
2楼-- · 2019-06-27 13:11

You could use the StackTrace and StackFrame to get the name of the current method

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

For properties, the returned method name will include the magic get_ or set_ prefixes.

However, I don't think you can really refactor this into an inline macro or function like you could in C++. But if you do refactor a utility method to DRY this out, you could probably just pop the StackTrace back one step to log the caller's information?

查看更多
甜甜的少女心
3楼-- · 2019-06-27 13:12

C#'s preprocessor doesn't support macros with associated values like C++, but what you're trying to do can be done with compilers that support C# 5.0 and greater (so at least VS2012+) through compiler generated Caller Information. Specifically, via the CallerMemberNameAttribute from the System.Runtime.CompilerServices namespace. Based on your question's code, I created the following example to illustrate how you could go about doing what you want to do:

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" will be printed to the console. When compiling, the compiler will replace the default value of GetCallerName's memberName argument with the the calling construct's (property, method, etc) name. So there's no code maintenance needed by the programmer

It should also be noted that this has the added benefit of being able to play nice with obfuscation tools, so long as they happen post-compilation.

查看更多
相关推荐>>
4楼-- · 2019-06-27 13:13

I don't know if there is something like a ViewBag in ASP.NET WebForms. Just in case there isn't, it isn't to difficult to roll you own. You can then wrap the ViewState in that class and get regular property member access like you wish.

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";
查看更多
登录 后发表回答