If attributes are created only when they are reflected, why are attribute constructors so limited?

As shown here , attribute constructors are not called until you decide to get the attribute values. However, as you can also know, you can only pass compilation constant values ​​to attribute constructors. Why is this? I think many people much prefer to do something like this:

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

than passing a string (calling strict input code too!) with these values ​​turning into strings and then relying on Regex to try to get the value instead of just using the actual value and instead of using compile-time warnings / errors depending on the exceptions that can be selected somewhere that has nothing to do with the class, except that the method it called uses some attributes that were not printed correctly.

What limitation caused this?

+6
source share
4 answers

Attributes are part of the metadata. You should be able to reflect metadata in an assembly without running code in that assembly .

Imagine, for example, that you are writing a compiler that needs to read attributes from an assembly in order to compile some source code. Do you really want the code in the link assembly to be downloaded and executed? Do you want to require compiler authors to write compilers that can run arbitrary code in referenced assemblies at compile time? Code that may crash or go into endless loops or contact databases that the developer does not have permission to talk to? The number of terrible scenarios is huge, and we eliminate them all, requiring the attributes to be simple.

+12
source

The problem is with the constructor arguments. They need to come from somewhere, they are not supplied with a code that consumes the attribute. They should be delivered using Reflection plumbing when it creates an attribute object by calling its constructor. To do this, he needs the values ​​of the constructor argument.

This starts at compile time when the compiler parses the attribute and writes the constructor arguments. It stores these argument values ​​in assembly metadata in binary format. The problem is that the runtime requires a highly standardized way to deserialize these values, which is preferably independent of any of the .NET classes that is commonly used to de-serialize data. Since there is no guarantee that such classes are indeed available at run time, they will not be in a very truncated version of .NET like the Micro Framework.

Even something as common as binary serialization with the BinaryFormatter class is troubling; note that this requires the [Serializable] attribute in the class so that it can do its job. The top would also be a huge problem, it is obvious that such a serializer class will never change for the risk of attribute violations in older assemblies.

This is rock and a difficult place, solved by CLS designers, severely restricting the valid types for the attribute constructor. They did not leave much, just simple value types, a string, a simple one-dimensional array of them, and a type. There is never a problem of deserializing them, because their binary representation is simple and unambiguous. Quite a limitation, but attributes can be quite expressive. The final failure is to use a string and decode this string in the constructor at run time. Creating a MyClass object is not a problem; you can do this in the attribute constructor. You will need to code the arguments that this constructor needs, however, as attribute properties.

+3
source

Probably the most correct answer about why you can use constants only for attributes is that the C # / BCL development team did not judge support for anything else important to add (i.e. not worth the effort).

When you create, the C # compiler will create the attributes that you put in your code and serialize them so that they can be stored in the generated assembly. Most likely, it was important to ensure fast and reliable retrieval of attributes than support for more complex scenarios.

In addition, code that crashes due to an incorrect attribute property value is much easier to debug than some internal deserialization error. See what happens if the class definition for MyClass was defined in an external assembly β€” you compile and paste one version, then update the class definition for MyClass and launch your application: boom!

On the other hand, it is seriously frustrating that DateTime instances are not constants.

0
source

What limitation caused this?

The reason you cannot do what you describe is probably not caused by any restriction, but it is purely a language design decision. Basically, when developing the language, they said: "It should be possible, but not that." If they really wanted this to be possible, the "limitations" would be resolved, and that would be possible. However, I do not know any concrete arguments in favor of this decision.

/.../ passing a string (calling strict input code too!) with these values, turning into strings, and then relying on Regex to try to get the value instead of just using the actual value /.../

I have been in similar situations. Sometimes I wanted to use attributes with lambda expressions to implement something in a functional way. But in the end, C # is not a functional language , and if I wrote the code in a non-functional way, I had no need for such attributes.

In short, I think so: if I want to develop this in a functional way, I have to use a functional language, for example f #. Now I use C #, and I do it non-functionally, and then I do not need such attributes.

Perhaps you should just rethink your design and not use attributes such as you currently have.

UPDATE 1:

I argued that C # is not a functional language, but it is a subjective opinion, and there is no strict definition of "Functional language". I agree with Adam Wright "/.../As such, I would not become the C # class as functional in the general discussion - this is, at best, a multi-paradigm with some functional taste." Why is C # a functional programming language?

UPDATE 2: I found this post from Jon Skeet: fooobar.com/questions/16374 / ... He finds it impossible to use common attribute types, but the reasoning may be similar in this case:

Eric Lippert's answer (rephrased): There is no particular reason, except to avoid the complexity of both the language and the compiler for use, which does not add much significance.

-1
source

Source: https://habr.com/ru/post/946325/


All Articles