6

如此处所示,您反映获取属性值之前,不会调用属性构造函数。但是,您可能也知道,您只能将编译时常量值传递给属性构造函数。为什么是这样?我想很多人愿意做这样的事情:

[MyAttribute(new MyClass(foo, bar, baz, jQuery)]

而不是传递带有这些值的字符串(也导致字符串类型的代码!),变成字符串,然后依靠正则表达式尝试获取值而不是仅仅使用实际值,而不是使用编译时警告/错误取决于关于可能在与类无关的地方抛出的异常,除了它调用的方法使用了一些输入错误的属性。

什么限制导致了这个?

4

4 回答 4

12

属性是元数据的一部分。您需要能够反映程序集中的元数据,而无需在该程序集中运行代码

例如,假设您正在编写一个编译器,该编译器需要从程序集中读取属性以编译一些源代码。您真的希望加载和执行引用程序集中的代码吗?您是否想对编译器编写者提出要求,即他们编写的编译器可以在编译期间在引用的程序集中运行任意代码?可能会崩溃、进入无限循环或联系开发人员无权与之交谈的数据库的代码?可怕的场景数量巨大,我们通过要求属性非常简单来消除所有这些场景。

于 2013-06-03T03:12:45.083 回答
3

问题在于构造函数参数。它们需要来自某个地方,它们不是由使用该属性的代码提供的。当反射管道通过调用其构造函数创建属性对象时,它们必须由反射管道提供。它需要构造函数参数值。

这从编译时开始,编译器解析属性并记录构造函数参数。它以二进制格式将这些参数值存储在程序集元数据中。那么问题在于,运行时需要一种高度标准化的方式来反序列化这些值,这种方式最好不依赖于您通常使用反序列化数据的任何 .NET 类。因为不能保证这些类在运行时实际上是可用的,所以它们不会像 Micro Framework 那样出现在非常精简的 .NET 版本中。

即使像 BinaryFormatter 类的二进制序列化这样常见的事情也很麻烦,请注意它如何需要类的 [Serializable] 属性才能完成其工作。版本控制也是一个巨大的问题,显然这样的序列化程序类永远不会改变,因为有破坏旧程序集中属性的风险。

这是一个非常困难的地方,CLS 设计者通过严格限制属性构造函数的允许类型来解决这个问题。他们没有留下太多,只是简单的值类型,字符串,它们的简单一维数组和类型。反序列化它们从来都不是问题,因为它们的二进制表示简单且明确。相当大的限制,但属性仍然可以很有表现力。最终的回退是使用字符串并在运行时在构造函数中解码该字符串。创建 MyClass 的对象不是问题,您可以在属性构造函数中这样做。但是,您必须将此构造函数需要的参数编码为属性的属性。

于 2013-06-02T13:49:16.617 回答
0

关于为什么只能对属性使用常量的最正确答案可能是因为 C#/BCL 设计团队没有判断是否支持任何其他重要到足以添加的内容(即不值得努力)。

当您构建时,C# 编译器将实例化您放置在代码中的属性并将它们序列化,以便它们可以存储在生成的程序集中。确保可以快速可靠地检索属性可能比支持更复杂的场景更重要。

此外,由于某些属性属性值错误而导致失败的代码比某些框架内部的反序列化错误更容易调试。考虑一下如果 MyClass 的类定义是在外部程序集中定义的——您编译并嵌入一个版本,然后更新 MyClass 的类定义并运行您的应用程序:繁荣!

另一方面,令人非常沮丧的是 DateTime 实例不是常量。

于 2013-06-01T21:09:06.410 回答
-1

什么限制导致了这个?

无法按照您的描述进行操作的原因可能不是由于任何限制造成的,但这纯粹是语言设计决定。基本上,在设计语言时,他们说“这应该是可能的,但不是这个”。如果他们真的希望这成为可能,那么“限制”就会得到解决,这将是可能的。我不知道这个决定背后的具体原因。

/.../ 使用这些值传递一个字符串(也导致字符串类型的代码!),转换为字符串,然后依靠正则表达式尝试获取值而不是仅使用实际值 /.../

我也遇到过类似的情况。我有时想使用带有 lambda 表达式的属性来以函数的方式实现某些东西。但毕竟 c# 不是函数式语言,如果我以非函数式的方式编写代码,我就不需要这些属性。

简而言之,我是这样想的:如果我想以函数式的方式开发它,我应该使用像 f# 这样的函数式语言。现在我使用 c# 并以非功能方式进行操作,然后我不需要这些属性。

也许您应该简单地重新考虑您的设计,而不是像现在这样使用属性。

更新 1:

我声称 c# 不是函数式语言,但这是一种主观观点,并且没有严格定义“函数式语言”。我同意 Adam Wright 的观点,“/.../ 因此,我不会在一般讨论中将 C# 归类为函数式的——它充其量是多范式,带有一些函数式的味道。” 在为什么 C# 是一种函数式编程语言?

更新 2:我发现 Jon Skeet 的这篇文章:https ://stackoverflow.com/a/294259/1105687它涉及不允许通用属性类型,但在这种情况下推理可能相似:

Eric Lippert 的回答(释义):没有特别的原因,除了避免语言和编译器的复杂性用于不会增加太多价值的用例。

于 2013-06-01T19:22:40.260 回答