【问题标题】:StackFrame behaving differently in release modeStackFrame 在发布模式下的行为不同
【发布时间】:2012-12-29 08:55:04
【问题描述】:

这是我的代码:

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 信息将在 Debug 构建配置中提供最多信息。默认情况下,Debug 构建包含调试符号,而 Release 构建不包含。调试符号包含大部分文件、方法名称、行号、和用于构造 StackFrame 对象的列信息。"
  • 这是否意味着..此代码在生产中会失败,因为在生产中它将被部署为发布模式???
  • 你为什么还要在 Release 中依赖 StackFrame() 呢?使用不同的设计!
  • 处理这样的用户偏好是繁重且过于复杂的。网上应该有很多例子:nini.sourceforge.net

标签: c# .net


【解决方案1】:

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

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

您实际上是在构建复杂的间接机制,而不是使用设计将信息传递给方法的语言特性——方法参数

为什么要使用属性?你在别处读过它们吗?

如果你这样做了,并且你不想重复"Email" 作为GetValue 调用和属性值的参数,你可以考虑将属性Expression&lt;&gt; 传递给GetValue,这将提取属性。这类似于您的解决方案,但它是明确的:

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

This answer shows how to implement this.

我看到您正在代码中检查Thread.CurrentPrincipal。同样,这不是一个非常好的做法,因为对于客户端代码而言,访问属性可能导致异常并不是显而易见。对于支持您的代码的人来说,这将是一场调试噩梦(相信我,your code may run for years in production, long after you move onto another project)。

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

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

【讨论】:

  • +1 - 达到目标。也可用于此类问题/设计选择是 MEF - mef.codeplex.com
【解决方案2】:

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

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

然后该属性可以不带参数调用 GetValue(),您将根据需要将属性名称传递给 GetValue()。

【讨论】:

    猜你喜欢
    • 2011-06-14
    • 2015-03-25
    • 1970-01-01
    • 2014-06-24
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多