我创建了一个名为RelatedPropertyAttribute
的Attribute
类:
[AttributeUsage(AttributeTargets.Property)]
public class RelatedPropertyAttribute: Attribute
{
public string RelatedProperty { get; private set; }
public RelatedPropertyAttribute(string relatedProperty)
{
RelatedProperty = relatedProperty;
}
}
我用它来表示类中的相关属性。我将如何使用它的示例:
public class MyClass
{
public int EmployeeID { get; set; }
[RelatedProperty("EmployeeID")]
public int EmployeeNumber { get; set; }
}
我想使用lambda表达式,这样我就可以把一个强类型传递给我的属性的构造函数,而不是一个“魔术字符串”。这样我就可以利用编译器的类型检查。例如:
public class MyClass
{
public int EmployeeID { get; set; }
[RelatedProperty(x => x.EmployeeID)]
public int EmployeeNumber { get; set; }
}
我想我可以用下面的代码来实现,但是编译器不允许这样做:
public RelatedPropertyAttribute<TProperty>(Expression<Func<MyClass, TProperty>> propertyExpression)
{ ... }
错误:
非泛型类型“RelatedPropertyAttribute”不能与类型参数一起使用
我怎样才能做到这一点?
7条答案
按热度按时间1l5u6lss1#
拥有泛型属性是不可能的。然而C#和VB不支持它,但CLR支持。如果你想写一些IL代码,这是可能的。
让我们看看你的代码:
编译代码,用ILSpy或ILDasm打开程序集,然后将内容转储到一个文本文件。属性类声明的IL将如下所示:
在文本文件中,您可以将属性设置为泛型。有几个方面需要更改。
这可以简单地通过更改IL来完成,CLR不会抱怨:
现在可以将 relatedProperty 的类型从string更改为泛型类型。
例如:
将其更改为:
有很多框架可以做这样的“肮脏”工作:Mono.Cecil或CCI中的任意一个。
正如我已经说过的,它不是一个干净的面向对象的解决方案,但只是想指出另一种方法来打破C#和VB的限制。
围绕这个主题有一本有趣的阅读check it out this book。
希望有帮助。
jtw3ybtb2#
你不能
[Foo<SomeType>]
)的语法fxnxkyjh3#
如果使用的是C# 6.0,则可以使用nameof
用于获取简单变量、类型或成员的(非限定的)字符串名称。当报告代码中的错误时,挂接模型视图控制器(MVC)链接、触发属性更改事件等,您通常希望捕获方法的字符串名称。使用nameof有助于在重命名定义时保持代码有效。在必须使用字符串常量引用定义之前,这在重命名代码元素时是脆弱的,因为工具不知道检查这些字符串文字。
你可以像这样使用你的属性:
t3psigkw4#
可能的解决方法之一是为每个属性关系定义类,并通过
属性构造函数中的typeof()运算符。
更新日期:
例如:
7hiiyaii5#
你不能。属性类型被限制为here。我的建议是,尝试在外部计算你的lambda表达式,然后使用以下类型之一:
hm2xizp96#
为了扩展我的评论,这是一种用不同的方法来完成任务的方法。您说您想要“指示类中的相关属性”,并且您“想要使用lambda表达式以便我可以将强类型传递到属性的构造函数中,而不是“魔术字符串”。这样我就可以利用编译器类型检查"。
下面是一种指示编译时类型化且没有任何幻字符串的相关属性的方法:
这是我们要考虑的类。我们希望指出
EmployeeId
和EmployeeNumber
是相关的。为了代码简洁,让我们把这个类型别名放在代码文件的顶部。这根本不是必须的,但它确实使代码不那么令人生畏:这使得
MyClassPropertyTuple
成为两个Expression
的Tuple
的别名,每个Expression
捕获从MyClass
到对象的函数的定义。例如,MyClass
上的属性getter就是这样的函数。现在让我们来捕捉这个关系,这里我在
MyClass
上做了一个静态属性,但是这个列表可以在任何地方定义:C#编译器知道我们正在构造
Expression
s的Tuple
,所以我们不需要在那些lambda表达式前面进行任何显式强制转换--它们会自动转换为Expression
s。基本上就定义而言就是这样了--**那些
EmployeeId
和EmployeeNumber
都是强类型的,并且在编译时被强制执行,而执行属性重命名的重构工具应该能够在重命名过程中找到这些用法(ReSharper当然可以)。当然,我们也希望能够在运行时查询关系(我假设!)。我不知道你想如何做到这一点,所以这段代码只是说明。
这里的
propertyInfo1FromExpression
和propertyInfo2FromExpression
代码是我在调试时明智地使用了Watch窗口编写的--通常我就是这样计算Expression
树实际包含的内容的。运行此程序将生成
这表明我们可以成功地提取相关属性的细节,而且(至关重要的是)* 它们与通过其他方式获得的
PropertyInfo
具有相同的引用 *。希望您可以将此方法与实际使用的任何方法结合使用,以在运行时指定感兴趣的属性。6ie5vjzr7#
提示:使用nameof。我有一个DateRangeAttribute,它验证两个属性并确保它们是有效的DateRange。