如何在属性中放置属性' A'知道财产的存在' B'?

时间:2016-01-13 17:21:02

标签: c# properties custom-attributes

如何让一个属性中的属性知道另一个属性的存在?

让我说我有这个班级,像这样,还有很多其他人:

public class MyClass
{
    [CheckDirty] //a custom attribute (that it is empty for now)
    public int A { get; set; }
    public int B { get; set; }

    public string Description { get; set; }
    public string Info { get; set; }
}

在我们程序的某个地方,如果我们想查看对象是否更改了任何CheckDirty属性的值,例如假设它与DB不同,MyPropertyUtils.GetPropertiesIfDirty()执行此操作,为我们提供一个数组在具有该属性的任何属性上更改了属性:

PropertyInfo[] MyPropertyUtils.GetPropertiesIfDirty(SomeBaseObject ObjectFromDB, SomeBaseObject NewValues);

完美。

所以,让我们说A已更改,在这种情况下Info包含我们需要的一些信息(在另一个类中可能是任何其他属性)。如果我们想要' A'我们只做property.GetValue(NewValues, null);

但我们不想要' A的价值,我们想要' A'或CheckDirty告诉我们在哪里阅读我们想要的一些数据。如何告诉我的属性CheckDirty从何处获取值?

我在考虑给CheckDirty一个表达式,但是一个属性的参数"必须是一个常量表达式,typeof表达式或属性参数类型的数组创建表达式"(那是VS说的)。 所以我决定,"好吧,让我们给它一个字符串,其中包含该属性的名称",所以我的尝试失败了:

(这是我们需要处理的所有代码,其余的只是提供某种上下文示例)

public class CheckDirty : Attribute
{
    public String targetPropertyName;

    public CheckDirty(String targetPropertyName)
    {
        this.targetPropertyName = targetPropertyName;
    }
}

public class MyClass
{
    //Code fails on this line
    [CheckDirty(BoundPropertyNames.Info)]
    public int Id { get; set; }

    public string Info { get; set; }

    public static class BoundPropertyNames
    {
        public static readonly string Info =   ((MemberExpression)
                                                        ((Expression<Func<MyClass, string>>)
                                                            (m => m.Info)
                                                        ).Body
                                                    ).Member.Name;
    }
}

这是我得到的错误:

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type  

我们不希望将该属性的名称作为String saing [CheckDirty("Info")]传递,因为如果将来任何人更改该类,并具体使用该属性的名称,编译时不会抛出任何错误,只会在运行时发生错误,当编辑&#34;编辑&#34;到那个领域会发生。或者也许它不会做任何事情,因为它无法找到属性。

知道如何不使用强类型字符串作为属性名称吗?

1 个答案:

答案 0 :(得分:1)

您可以使用类似的东西,首先声明一个接口,该接口将由需要进行脏检查的每个类实现:

SELECT IIF(clock_to - clock_at_from < 0,
           IIF(12 - (clock_at_from - clock_to) >= 3, 5, 12 - (clock_at_from - clock_to)),
           clock_to - clock_at_from) as clock_pos
FROM Conditions

然后像那样实现它

interface IDirtyCheckPropertiesProvider {
   string GetPropertyName(string dirtyProperty);
}

在负责处理脏检查的类中,必须使用此接口来获取目标属性名称。 使用Reflection API可以进一步删除有太多的样板。

另一方面,使用字符串作为属性名称看起来更简单的解决方案。如果使用像Resharper这样的工具 - 使用字符串是一个可行的选项 - 当您更改属性名称时,Resharper将自动重构字符串。 同样很长一段时间,字符串的属性名称被用于实现WPF INotifyPropertyChanged。

评论建议class DataEntity : IDirtyCheckPropertiesProvider { [CheckDirty] public int Id { get; set; } public string Info { get; set; } string GetPropertyName(string dirtyProperty) { if (GetPropertyNameFromExpression(x => Id) == dirtyProperty) return GetPropertyNameFromExpression(x => Info); return null; } } 是VS2015的最佳选择。