在发布模式下,StackFrame 的行为与调试模式下不同

10

这是我的代码:

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
StackFrame信息在Debug构建配置下最为详细。默认情况下,Debug构建包含调试符号,而Release构建则不包含。调试符号包含大部分文件、方法名、行号和列信息,用于构建StackFrame对象。 - Mitch Wheat
这是否意味着...这段代码在生产环境中会失败,因为它将作为发布模式部署? - dotnet-practitioner
3
在发布版本中,为什么还要依赖于StackFrame()呢?使用不同的设计吧! - Mitch Wheat
处理用户偏好设置的方式过于笨重和过于复杂。网上应该有许多例子:http://nini.sourceforge.net/ - Mitch Wheat
你可以考虑使用MEF - http://msdn.microsoft.com/zh-cn/VS2010TrainingCourse_IntroToMEF.aspx - Hogan
2个回答

8
我曾经回答过类似的问题,请在这里阅读我的回答
简而言之,这是一个非常糟糕的设计决策,因为你的方法是个伪君子——对不同的调用者说不同的话,但并没有公开地告诉它。你的 API 应该永远不要依赖于谁调用它。此外,编译器可能会由于语言特性(如 lambda、yieldawait)而以意想不到的方式中断堆栈跟踪,因此即使在 Release 模式下这个方法可行,也肯定会有一天出现问题。
你实际上正在构建一个复杂的间接机制,而不是使用专门设计用于将信息传递给方法的语言特性——方法参数
你为什么要使用属性?你是否在其他地方读取它们?
如果是这样,并且你不想在 GetValue 调用的参数和属性值中重复使用 "Email",你可以考虑将 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#中有许多经过测试的高性能解决方案用于存储和读取配置。你为什么认为需要重新发明轮子呢?


1
+1 - 目标达成。此外,针对这种问题/设计选择可使用MEF - http://mef.codeplex.com/ - Hogan

5
假设在讨论是否可以使用其他库而不是自己编写时,您的问题得以保留……如果您正在使用C# 5和.NET 4.5,请查看CallerMemberName属性。使用CallerMemberName,您可以修改GetValue()方法签名为:
public static string GetValue([CallerMemberName] string callerName = "")

该属性可以调用GetValue()方法而不需要参数,你将得到传递给GetValue()方法的属性名称。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接