StackFrame在发布模式下表现不同

时间:2012-12-28 23:39:12

标签: c# .net

这是我的代码:

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?

2 个答案:

答案 0 :(得分:8)

我曾回答过一个类似的问题,请read my answer here

简而言之,这是一个非常糟糕的设计决策,因为你的方法是一个伪君子 - 它与不同的调用者说话不同但不公开告诉它。您的API 永远不会依赖于谁调用它。此外,由于lambda,yieldawait等语言功能,编译器可能会以意外的方式中断堆栈跟踪,因此即使在Release模式下工作,它肯定会在某一天中断。

您正在有效地构建复杂的间接机制,而不是使用语言功能设计将信息传递给方法 - 方法参数

为什么使用属性?你在其他地方看过吗?

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

GetValue

This answer shows how to implement this.

我看到您正在检查代码中的[UserPreferenceProperty(Category = "Email", DefaultValue = "My default value")] public string Signature { get { return GetValue (prefs => prefs.Signature); } set { SetValue (prefs => prefs.Signature, value); } } 。同样,这不是一个非常好的做法,因为访问属性的客户端代码不是明显的可能导致异常。对于支持您的代码的人来说,这将是一个调试噩梦(并相信我,your code may run for years in production, long after you move onto another project)。

相反,您应该为设置类构造函数设置Thread.CurrentPrincipal 参数。这将确保调用代码知道您在此级别上强制执行安全性,并且根据定义知道在何处获取此令牌。此外,这允许您在知道错误时立即抛出异常,而不是在访问某些属性时。这将有助于维护者更早地捕获错误 - 就像编译错误比运行时错误更好。

最后,虽然这是一项有趣的练习,但有plenty performant and tested solutions for storing and reading configuration in C#。为什么你认为你需要重新发明轮子?

答案 1 :(得分:5)

假设您的问题在讨论是否可以使用其他库而不是滚动自己的问题时仍然存在...如果您发现自己使用的是C#5&amp; .NET 4.5,请查看CallerMemberName属性。使用CallerMemberName,您可以将GetValue()方法签名修改为

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

然后该属性可以调用没有参数的GetValue(),你可以根据需要将属性名称传递给GetValue()。