StackFrame在释放模式下的行为不同

本文关键字:释放 模式 StackFrame | 更新日期: 2023-09-27 17:49:44

下面是我的代码:

public class UserPreferences
{
    /// <summary>
    /// The EMail signature.
    /// </summary>
    [UserPreferenceProperty(Category = "Email", DefaultValue = "My default value")]
    public static string Signature
    {
        get
        {
            return UserPreferenceManager.GetValue();
        }
        set
        {
            UserPreferenceManager.SetValue(value);
        }
    }
}
public static string GetValue()
{
    if (((VTXPrincipal)Thread.CurrentPrincipal).VTXIdentity.OperatorID == null)
    {
        throw new Exception("Missing Operator ID");
    }
    string value = string.Empty;
    var frame = new StackFrame(1);  ***** <------ problem here.....
    var property = frame.GetMethod();
    var propertyname = property.Name.Split('_')[1];
    var type = property.DeclaringType;   ***** <------ problem here.....
    if (type != null)
    {
        var userPreference = typeof(UserPreferences).GetProperty(propertyname).GetCustomAttributes(true).FirstOrDefault() as UserPreferencePropertyAttribute;
        if (userPreference != null)
        {
            string category = userPreference.Category;
            string description = propertyname;
            value = GetValue(category, description, ((VTXPrincipal)Thread.CurrentPrincipal).VTXIdentity.OperatorID);
            if (value == null)
            {
                // always return something
                return userPreference.DefaultValue;
            }
        }
        else
        {
            throw new Exception("Missing User Preference");
        }
    }
    return value;
}

在GetValue方法内部,StackFrame在发布模式和调试模式下的工作方式不同。

在调试模式下,我正确地获得属性名作为签名

但是在发布模式下,属性名称是GetUserPreferenceValueTest,因为这是作为客户端进行调用的测试方法。

因此我的代码在调试模式下工作,但在发布模式下失败。

Q. How can I use StackFrame properly so it works in Debug vs. Release modes. 
Q. Is there any other way to get calling property name and related information at run time?

StackFrame在释放模式下的行为不同

我曾经回答过一个类似的问题,请在这里阅读我的回答。

简而言之,这是一个非常糟糕的设计决策,因为您的方法是一个伪君子——它对不同的调用者说不同的话,但不公开地告诉它。你的API应该永远不要依赖于调用它的人。此外,由于语言特性,如lambdas, yieldawait,编译器可以以一种意想不到的方式中断堆栈跟踪,所以即使它在发布模式下工作,也肯定有一天会中断。

你有效地构建了一个复杂的间接机制,而不是使用语言特性设计的来传递信息给方法- 方法参数

为什么要使用属性?你在别的地方读过吗?

如果您这样做,并且您不想重复"Email"作为GetValue调用的参数和属性值,您可以考虑将属性Expression<>传递给GetValue,这将提取该属性。这与您的解决方案类似,但它是显式的:

[UserPreferenceProperty(Category = "Email", DefaultValue = "My default value")]
public string Signature
{
    get { return GetValue (prefs => prefs.Signature); }
    set { SetValue (prefs => prefs.Signature, value); }
}

这个答案展示了如何实现它。

我看到你正在检查Thread.CurrentPrincipal在你的代码。同样,这不是一个真正的好做法,因为对于客户端代码来说,访问属性可能导致异常的并不明显。对于支持你的代码的人来说,这将是一场调试噩梦(相信我,你的代码可能在生产环境中运行数年,在你转移到另一个项目之后很长时间)。

相反,您应该使VTXIdentity 成为您的设置类构造函数的参数。这将确保调用代码知道在此级别上强制安全性,并且根据定义知道从何处获得此令牌。此外,这允许您在知道有错误时抛出异常,而不是在访问某些属性时。这将帮助维护人员更早地捕获错误——就像编译错误比运行时错误要好。

最后,虽然这是一个有趣的练习,但在c#中存储和读取配置有很多性能良好且经过测试的解决方案。你为什么认为你需要重新发明轮子?

假设您的问题存在于是否可以使用另一个库而不是滚动自己的库的讨论中…如果你发现自己在使用c# 5 &. NET 4.5,看一下CallerMemberName属性。使用CallerMemberName,您可以将GetValue()方法签名修改为

public static string GetValue([CallerMemberName] string callerName = "")

属性可以不带参数地调用GetValue(),你会得到你想要的属性名传递给GetValue()。