Meeting the needs of your business from a distance

Prevent Duplicate Extension Method Signatures

by Mark Shiffer 9. March 2009 17:17

There are pluses and minuses to the use of Extension Methods as implemented by the Microsoft. Per there own guidelines Microsoft recommends to implement extension methods sparingly and only when you have to. Whenever possible, client code that must extend an existing type should do so via inheritance or other means.

That being said, one of the issues that I have with Extension Methods is that they compile fine even when the signature matches that of the base class it is extending. In this scenario, the extension method will never be called. In my opinion this should be a compiler error, thus I have created a Code Analysis rule to prevent extension methods that have the same signature as a method on the class it is extending. Here is a link to it: MSSWC.CodeAnalysis.MaintainabilityRules

To install this rule, simply copy the dll into your FxCop\Rules folder. Here is the source for the rule if you want to do your own spin on it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.FxCop.Sdk;
using System.Runtime.CompilerServices;
using System.Reflection;
 
namespace MSSWC.CodeAnalysis.MaintainabilityRules
{
    /// <summary>
    /// Generate warning/error when method signature of an extension method matches one that already exists on the 
    /// base class.
    /// </summary>
    internal sealed class DoNotDuplicateBaseClassMethodSignatureInExtensionMethod : MaintainabilityRule
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DoNotDuplicateBaseClassMethodSignatureInExtensionMethod"/> class.
        /// </summary>
        public DoNotDuplicateBaseClassMethodSignatureInExtensionMethod()
            : base("DoNotDuplicateBaseClassMethodSignatureInExtensionMethod")
        {
 
        }
 
        /// <summary>
        /// Members has an attribute of the given type.
        /// </summary>
        /// <param name="member">The member.</param>
        /// <param name="typeToCheck">The type to check.</param>
        /// <returns>True if member has an attribute of typeToCheck</returns>
        private static bool MemberHasAttribute(Member member, Type typeToCheck)
        {
            foreach (AttributeNode attrib in member.Attributes)
            {
                if (attrib.Type.FullName == typeToCheck.FullName)
                {
                    return true;
                }
            }
 
            return false;
        }
 
 
        /// <summary>
        /// Returns whether the baseType has a method signature that matches the passed in methodSignature.
        /// </summary>
        /// <param name="baseType">Type of the base.</param>
        /// <param name="methodSignature">The method signature.</param>
        /// <returns>True if baseType has a method signature that matches methodSignature.</returns>
        private static bool TypeHasMethodSignature(TypeNode baseType, string methodSignature)
        {
            var result = from m in baseType.Members
                         where m.FullName == methodSignature
                         select m.FullName;
 
            return result.Count() > 0;
        }
 
        /// <summary>
        /// Checks the specified member for problems.
        /// </summary>
        /// <param name="member">The member.</param>
        /// <returns></returns>
        public override ProblemCollection Check(Member member)
        {
            Method method = member as Method;
            if (method != null)
            {
                if (MemberHasAttribute(member, typeof(ExtensionAttribute)))
                {
                    // Get first parameter's type which is the type that we are extending
                    TypeNode node = method.Parameters[0].Type;
 
                    // build array of the parameters for the extension method
                    string parameters = "";
 
                    for (int i = 1; i < method.Parameters.Count; i++)
                    {
                        parameters += method.Parameters[i].Type.FullName + ",";
                    }
 
                    parameters = parameters.Trim(',');
 
                    // build method signature string
                    string methodSignature = node.FullName + "." + member.Name.Name;
                    if (!string.IsNullOrEmpty(parameters))
                    {
                        methodSignature += "(" + parameters + ")";
                    }
 
                    // ensure method signature does not already exist on base class
                    if (TypeHasMethodSignature(node, methodSignature))
                    {
                        Resolution resolution = GetNamedResolution("Default", 
                            new object[] { member.FullName, methodSignature });
 
                        Problems.Add(new Problem(resolution, method.SourceContext));
                    }
                }
            }
 
            return Problems;
        }
    }
}

Tags:

Programming | Tools

Comments

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading



Copyright © 2001-2010 MS Consulting, Inc. All Rights Reserved.