Attributes

Another useful extension to the serialization method is the exclusion of certain fields; not all fields of a type should be serialized. Like most other .NET languages, C++/CLI does not have a keyword to express that a field should not be serialized. However, .NET allows you to extend languages so that you can provide additional metadata to types, type members, and various other targets of your assembly. This can be done with attributes.

Attributes in .NET languages have a syntax that is very similar to the Interface Definition Language (IDL) syntax for attributes in COM. An attribute is specified within square brackets and (typically) applies to the item that follows the attribute. As an example, the following code could be used to apply a DoNotSerializeThisField attribute to a field.

ref struct Person {

StringA Name;

// not everybody wants that his age is stored somewhere

[DoNotSerializeThisField]

int Age;

Supporting such an attribute is quite simple. It only requires a class with the special base class System::Attribute.

ref class DoNotSerializeThisFieldAttribute : public Attribute {};

Notice that the DoNotSerializeThisFieldAttribute class follows a common naming convention for attribute classes—it has the suffix Attribute. Due to this naming convention, it is sufficient in C++/CLI and most other .NET languages to write [DoNotSerializeThisField] instead of [DoNotSerializeThisFieldAttribute].

An attribute, as it is defined in the following example, can be applied to many targets. The following code shows some typical examples:

using namespace System;

ref class DoNotSerializeThisFieldAttribute : public Attribute {};

[assembly: DoNotSerializeThisFieldAttribute];

[DoNotSerializeThisField]

ref class ATypeDefinition {

[DoNotSerializeThisField] int field1;

[DoNotSerializeThisField] // applied

[returnvalue: DoNotSerializeThisField] // applied int func2() { return 0; }

Obviously, the DoNotSerializeThisField attribute can only be applied to fields in a useful way. None of the other applications (to the assembly, to the class ATypeDefinition, or to the method func2 and its return value) makes sense. To restrict the usage of the attribute class so that it can only be applied to fields, it can be defined as follows:

[AttributeUsage(AttributeTargets::Field, AllowMultiple=false)]

ref class DoNotSerializeThisFieldAttribute : public Attribute {};

In this code, System::AttributeUsageAttribute is applied to the attribute class. Notice that this attribute is applied with further information provided in brackets. In the preceding sample code, AttributeTargets::Field means that the attribute can only be applied to fields, and AllowMultiple = false means that the attribute cannot be applied more than once to a single field.

If the possible targets included type definitions or methods, it could also have been useful to write Inherit = true or Inherit = false within the brackets of the attribute. Using these options, one can specify if the attribute is inherited to derived types or overloaded methods.

AttributeTargets::Field is passed like a function call argument, whereas AllowMultiple=false is passed with an assignment-like syntax. The attribute can be applied like this because it implements a constructor expecting an argument of type AttributeTargets and a Boolean property named AllowMultiple.

To ensure that a field with this attribute is not serialized, the attribute must be discovered at runtime. This can be done with the interface System::Reflection::ICustomAttributeProvider. All classes from the Managed Reflection API that provide metadata for attribute targets implement this interface. These classes include System::Type as well as Assembly, FieldInfo, and MethodInfo from the System::Reflection namespace. ICustomAttributeProvider has the method IsDefined, which can be used to check if an attribute was applied or not. This method to the following method to return type of method returns true if the requested attribute is applied to the target. IsDefined expects a type info object of the requested attribute class and a Boolean parameter that specifies if attributes inherited from a base class or an overridden virtual method should be considered or not. For FieldInfo objects, this second parameter of IsDefined is not relevant.

array<FieldInfoA>A fields = t->GetFields();

for each (FieldInfoA fi in fields) {

Was this article helpful?

0 0

Post a comment