如果仅在反映属性时构造属性,为什么属性构造函数如此受限?

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

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

而不是传递一个字符串(导致字符串类型的代码!)与这些值,变成字符串,然后依靠Regex尝试获取值而不是仅使用实际值,而不是使用编译时警告/错误取决于可能会抛出与该类无关的exception,除了它调用的方法使用一些键入错误的属性。

这有什么限制?

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

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

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

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

即使是像BinaryFormatter类的二进制序列化这样常见的东西也很麻烦,请注意它如何要求类上的[Serializable]属性允许它完成它的工作。 版本控制也是一个巨大的问题,显然这样的序列化程序类永远不会因为破坏旧程序集中的属性而发生变化。

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

关于为何只能使用常量来表示属性的最正确的答案可能是因为C#/ BCL设计团队没有判断是否支持任何其他重要的东西(即不值得付出努力)。

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

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

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

这有什么限制?

你不可能做你所描述的事情的原因可能不是由任何限制引起的,但它纯粹是一种语言设计决定。 基本上,在设计语言时,他们说“这应该是可能的,但不是这个”。 如果他们真的希望这是可能的,那么“限制”就会被处理掉,这是可能的。 我不知道这个决定背后的具体原因。

/…/传递一个字符串(导致字符串类型的代码!)与这些值,变成字符串,然后依靠Regex尝试获取值而不是仅使用实际值/…/

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

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

也许您应该只是重新考虑您的设计而不是像目前那样使用属性。

更新1:

我声称c#不是一种function语言,但这是一种主观观点,并没有严格的“function语言”定义。 我同意亚当·赖特的观点,“/。。/因此,我不会将C#作为一般性讨论中的function – 它充其量只是多范式,具有一定的function性。” 为什么C#是一种function性编程语言?

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

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