StackFrame在发布模式下表现不同

这是我的代码:

public class UserPreferences { ///  /// The EMail signature. ///  [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? 

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

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

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

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

如果你这样做,并且你不想重复"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 。 同样,这不是一个非常好的做法,因为对于客户端代码而言,访问属性可能导致exception并不明显 。 对于支持您的代码的人来说,这将是一个调试噩梦(并且相信我, 您的代码可能会在生产中运行多年,在您迁移到另一个项目之后很久 )。

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

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

假设你的问题仍然存在,你是否可以使用另一个库而不是滚动自己的问题……如果你发现自己使用的是C#5和.NET 4.5,请查看CallerMemberName属性。 使用CallerMemberName,您可以修改GetValue()方法签名

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

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