如图here所示,在您反映获取属性值之前,不会调用属性构造函数。但是,您可能也知道,只能将编译时常量值传递给属性构造函数。为什么是这样?我想很多人会很多喜欢做这样的事情:
[MyAttribute(new MyClass(foo, bar, baz, jQuery)]
传递一个字符串(导致字符串类型的代码!)与这些值,转换为字符串,然后依靠Regex尝试获取值而不是仅使用实际值,而不是使用编译时警告/ errors取决于可能抛出与该类无关的异常,除了它调用的方法使用了一些键入错误的属性。
这导致了什么限制?
答案 0 :(得分:12)
属性是元数据的一部分。您需要能够反映在程序集中的元数据,而无需在该程序集中运行代码。
想象一下,例如,您正在编写一个需要从程序集中读取属性的编译器,以便编译一些源代码。你真的希望引用程序集中的代码加载并执行吗?您是否希望在编译器编写器上放置一个需求,他们编写的编译器可以在编译期间在引用的程序集中运行任意代码?代码可能崩溃,或进入无限循环,或联系开发人员没有权限与之交谈的数据库?可怕场景的数量是巨大的,我们通过要求属性变得简单而消除所有这些场景。
答案 1 :(得分:3)
问题在于构造函数参数。它们需要来自某处,它们不是由使用该属性的代码提供的。它们必须由Reflection plumbing在通过调用其构造函数创建属性对象时提供。它需要构造函数参数值。
这是在编译时从编译器解析属性并记录构造函数参数开始的。它以二进制格式将这些参数值存储在程序集元数据中。问题在于运行时需要一种高度标准化的方法来反序列化这些值,最好不依赖于您通常使用de / serialize数据的任何.NET类。因为不能保证这些类在运行时实际可用,所以它们不会像Micro Framework那样处于非常精简的.NET版本中。
即使像BinaryFormatter类的二进制序列化这样常见的东西也很麻烦,请注意它需要类的[Serializable]属性来允许它完成它的工作。版本控制也是一个巨大的问题,显然这样的序列化程序类可能永远不会因为破坏旧程序集中的属性而发生变化。
这是一个摇滚和困难的地方,由CLS设计者通过严格限制属性构造函数的允许类型来解决。它们没有留下太多,只是简单的值类型,字符串,它们的简单一维数组和Type。从来没有问题反序列化它们,因为它们的二进制表示简单明了。相当一个限制,但属性仍然可以很有表现力。最终的回退是在运行时使用字符串并在构造函数中解码该字符串。创建MyClass的对象不是问题,您可以在属性构造函数中执行此操作。您必须将此构造函数所需的参数编码为属性的属性。
答案 2 :(得分:0)
关于为什么你只能使用常量来表示属性的最正确的答案是因为C#/ BCL设计团队没有判断是否支持任何其他重要的东西(即不值得付出努力)。
构建时,C#编译器将实例化您在代码中放置的属性并对其进行序列化,以便它们可以存储在生成的程序集中。确保可以快速可靠地检索属性比支持更复杂的场景更重要。
此外,由于某些属性属性值错误而失败的代码比某些框架内部反序列化错误更容易调试。考虑如果在外部程序集中定义了MyClass的类定义会发生什么 - 您编译并嵌入一个版本,然后更新MyClass的类定义并运行您的应用程序:boom!
另一方面,DateTime实例不是常量非常令人沮丧。
答案 3 :(得分:-1)
这导致了什么限制?
不可能做你所描述的内容的原因可能不是由任何限制造成的,但它纯粹是一种语言设计决定。基本上,在设计语言时,他们说“这应该是可能的,但不是这个”。如果他们真的希望这是可能的,那么“限制”就会被处理掉,这是可能的。我不知道这个决定背后的具体原因。
/.../传递一个字符串(导致字符串类型的代码!)与这些值,变成字符串,然后依靠Regex尝试获取值而不是仅使用实际值/.../
我一直处于类似情况。我有时想使用带有lambda表达式的属性来以功能方式实现某些东西。但毕竟,c#不是functional language,如果我以非功能性的方式编写代码,我就不需要这些属性了。
简而言之,我想是这样的:如果我想以功能的方式开发它,我应该使用像f#这样的函数式语言。现在我使用c#并以非功能方式执行,然后我不需要这些属性。
也许您只需重新考虑您的设计,而不是像目前那样使用属性。
更新1:
我声称c#不是一种功能语言,但这是一种主观观点,并没有严格的“功能语言”定义。我同意亚当·赖特的观点,“/。。/因此,我不会将C#作为一般性讨论中的功能 - 它充其量只是多范式,具有一定的功能性。”在Why is C# a functional programmming language?
更新2: 我发现Jon Skeet的这篇文章:https://stackoverflow.com/a/294259/1105687它认为不允许通用属性类型,但在这种情况下推理可能类似:
Eric Lippert的回答(转述):没有特别的理由,除了 避免用例的语言和编译器的复杂性 这不会增加太多价值。