Jagamisviis:


Creating Policy Injection Matching Rules

Unity defines an interface named IMatchingRule, which all classes that implement matching rules must implement. This interface declares a single method named Matches that takes a MethodBase instance and returns a Boolean value.

public interface IMatchingRule
{
  bool Matches(MethodBase member);
}
'Usage
Public Interface IMatchingRule
  Function Matches(ByVal member As MethodBase) As Boolean
End Interface

Inside a concrete implementation of this interface, a custom matching rule class can access details of the current member (method or property) of the target object and determine whether Unity should add a handler pipeline to this member. The Matches method should return True if the current member matches the requirements of this matching rule; if the current member does not match the requirements of this matching rule, it should return False.

Note

Remember that there may be several matching rules defined for a policy, and every one must return True when Unity calls their Matches method in order for Unity to add the handler pipeline to the target class member.

Example: The TagAttributeMatchingRule

As an example of a matching rule, this following extract shows the TagAttributeMatchingRule class that implements the built-in tag attribute matching rule. This rule examines the current target object member, passed to the Matches method as a parameter, looking for any attributes of type TagAttribute, whose value matches the string value passed to the class constructor.

[ConfigurationElementType(typeof(TagAttributeMatchingRuleData))]
public class TagAttributeMatchingRule : IMatchingRule
{
  private readonly string tagToMatch;
  private readonly bool ignoreCase;

  // This constructor takes only the tag value.  
  public TagAttributeMatchingRule(string tagToMatch)
       : this(tagToMatch, false)
  { }

  // This constructor takes the tag value and the case-sensitivity setting. 
  public TagAttributeMatchingRule(string tagToMatch, bool ignoreCase)
  {
    this.tagToMatch = tagToMatch;
    this.ignoreCase = ignoreCase;
  }

  // The returns true only if a matching TagAttribute exists on this member.
  public bool Matches(MethodBase member)
  {
    foreach (TagAttribute tagAttribute in 
             ReflectionHelper.GetAllAttributes<TagAttribute>(member, true))
    {
      if (string.Compare(tagAttribute.Tag, tagToMatch, ignoreCase) == 0)
      {
        return true;
      }
    }
    return false;
  }
}
'Usage
<ConfigurationElementType(GetType(TagAttributeMatchingRuleData))> _
Public Class TagAttributeMatchingRule : Implements IMatchingRule

  Dim tagToMatch As String
  Dim ignoreCase As Boolean

  ' This constructor takes only the tag value.  
  Public Sub New(ByVal tagToMatch As String)
    Me.New(tagToMatch, false)
  End Sub

  ' This constructor takes the tag value and the case-sensitivity setting. 
  Public Sub New(ByVal tagToMatch As String, ByVal ignoreCase As Boolean)
    Me.tagToMatch = tagToMatch
    Me.ignoreCase = ignoreCase
  End Sub

  ' The returns true only if a matching TagAttribute exists on this member.
  Public Function Matches(ByVal member As MethodBase) As Boolean Implements IMatchingRule.Matches
    For Each tagAttribute As TagAttribute In _
        ReflectionHelper.GetAllAttributes(Of TagAttribute)(member, true)
      If string.Compare(tagAttribute.Tag, tagToMatch, ignoreCase) = 0 Then
        Return True
      End If
    Next
    Return False
  End Sub
End Class

When Unity first creates an instance of the target object, it reads a list of matching rules from the configuration. For each rule it finds, it creates an instance of the appropriate class and passes to it as parameters the values of any attributes defined within the configuration for that matching rule. You can see that the constructors for the TagAttributeMatchingRule class accept the name of the attribute and a case-sensitivity value.

Internally, the TagAttributeMatchingRule class uses a separate helper class named ReflectionHelper that gets a list of all the attributes of type TagAttribute on the target member so that the Matches method can detect any that has the specified value—taking into account the setting of the ignoreCase property for this handler instance.

Notice that the TagAttributeMatchingRule class has a ConfigurationElementType attribute that defines the class (TagAttributeMatchingRuleData) that the configuration system uses to expose the data from the configuration to the matching rule. As an alternative approach, you can also use the CustomMatchingRuleData class in this attribute. This eliminates the need to create your own configuration data class; however, configuration information will be provided as a dictionary and will not be strongly typed.