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?
4

2 回答 2

8

我曾经回答过类似的问题,请在此处阅读我的回答

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

您正在有效地构建一个复杂的间接机制,而不是使用为将信息传递给方法而设计的语言特性——<strong>方法参数。

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

如果您这样做,并且您不想重复"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# 中存储和读取配置有很多高性能且经过测试的解决方案。为什么你认为你需要重新发明轮子?

于 2012-12-29T00:30:29.763 回答
5

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

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

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

于 2012-12-29T00:55:04.063 回答