Having multiple instances of the same custom attribute on the same target is sometimes a desired behavior. With multicasting attributes, it is easy to end up with that situation: many multicasting paths can lead to the same target.
However, most of the time, we would prefer a different behavior. We could define a method-level aspect on the type (this aspect would apply to all methods) and override the aspect on a specific method. What we require here is the possibility to replace an instance of a custom attribute by another. This scenario is fully supported by the multicasting framework.
The multicasting framework has both the ability to apply multiple custom attribute instances on the same target, and the ability to replace or override custom attributes. Note that we are here talking of custom attributes of the same type.
Applying multiple instances of the same custom attribute
The attribute developer can allow users to apply multiple
instances of the same custom attribute by setting the
AllowMultiple property of the MulticastAttributeUsageAttribute
custom attribute to true:
[MulticastAttributeUsage( MulticastTargets.Class, AllowMultiple = true )]
public class MyAttribute : MulticastAttribute
{
public field string Tag;
}
For instance, the following code results in three instance of the custom attribute:
[assembly: MyAttribute( AttributeTypes = "My*", Tag = "A" )]
[assembly: MyAttribute( AttributeTypeAttributes = MulticastAttributes.Public, Tag = "B" )]
[MyAttribute( Tag = "C" )]
public class MyClass {}
Deleting instances of a custom attribute
The AttributeExclude property of the MulticastAttribute
class allows to remove any previous instance of the same custom
attribute on a target.
This is useful, for instance, when you need to exclude a target from the matching set of a wildcard expression. For instance:
[assembly: Configurable( AttributeTypes = "BusinessLayer.*" )]
namespace BusinessLayer
{
[Configurable( AttributeExclude = true )]
public static class Helpers
{
}
}
Priority of custom attributes
Since the AttributeExclude custom attribute causes
any previous custom attribute to be excluded, we need to precise
the notion of precedence of custom attributes. Indeed, how can a
custom attribute precede another one, when standard .NET attributes
are unordered.
In order to disambiguate this issue, we have added a notion of priority to multicast custom attributes. The following rules apply:
- Custom attribute instances defined on a container have always precedence over instances defined on an element of that container. Therefore, an exclusion defined on a class is stronger than a addition defined on an assembly, which explains the example above.
- When custom attribute instances are defined on the same level,
they are sorted by increasing of priority. The priority is defined
by the
AttributePriorityproperty of the MulticastAttribute class.
Therefore, the following example is equivalent to the previous one:
[assembly: Configurable( AttributeTypes = "BusinessLayer.*", AttributePriority = 1 )]
[assembly: Configurable( AttributeTypes = "BusinessLayer.Helpers",
AttributeExclude = true, AttributePriority = 2 )]
namespace BusinessLayer
{
class Helpers {}
}
Replacing instances of a custom attribute
Custom attribute replacement is enabled by the
AttributeReplace property of the MulticastAttribute
class. It is equivalent to a deletion followed by an insertion.
When it is not allowed to have multiple instances of the same custom attributes on a single target, the default behavior of multicast attributes is that the custom attribute with greater priority replaces the custom attribute of lower priority.
For instance, the following code applies a tag
BusinessObject to all classes of the
BusinessLayer namespace, expect to the class
Helpers, to which the tag Helper is
applied:
[assembly: Tag( "BusinessObject",
AttributeTypes = "BusinessLayer.*",
AttributePriority = 1 )]
[assembly: Tag( "Helper",
AttributeTypes = "BusinessLayer.Helpers",
AttributePriority = 2 )]