Friday, August 15, 2008

Checking For Duplicate Classes

Here's a solution to a problem I've frequently seen online. Sometimes, your code just seems drunk. You get type mismatches all over the place, and you're racking your brain to figure out why. Here's a class you can use to see if you've inadvertantly put duplicate class names in your code or in assemblies referenced by your code.

It does three things:

1. Gathers the assembly names of all the related assemblies to the one you're running in, even down to mscorlib.dll.
2. Gathers information on every Type in all of those assemblies.
3. Returns the types that match a particular name.



   1:  public static class DuplicateClassFinder

   2:  {

   3:      private static List<string> CheckDuplicates(List<string> classNames, List<string> exploredAssemblies, Assembly assembly)

   4:      {

   5:          if (exploredAssemblies.Contains(assembly.FullName))

   6:              return new List<string>();

   7:          exploredAssemblies.Add(assembly.FullName);

   8:   

   9:          Console.WriteLine(assembly.FullName);

  10:          List<string> result = new List<string>();

  11:          foreach (Type t in assembly.GetTypes())

  12:          {

  13:              if (classNames.Contains(t.Name))

  14:              {

  15:                  result.Add(t.Name);

  16:              }

  17:              classNames.Add(t.Name);

  18:          }

  19:          AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies();

  20:   

  21:          foreach (AssemblyName assemblyName in assembly.GetReferencedAssemblies())

  22:          {

  23:              result.AddRange(CheckDuplicates(classNames, exploredAssemblies, Assembly.Load(assemblyName)));

  24:          }

  25:          return result;

  26:      }

  27:   

  28:      private static void GetReferencedAssemblyTree(ref List<string> assemblyNames, AssemblyName assemblyName)

  29:      {

  30:          if (assemblyName == null)

  31:              assemblyName = Assembly.GetExecutingAssembly().GetName();

  32:          if (assemblyNames == null)

  33:              assemblyNames = new List<string>();

  34:   

  35:          if (assemblyNames.Contains(assemblyName.FullName))

  36:              return;

  37:   

  38:          assemblyNames.Add(assemblyName.FullName);

  39:   

  40:          foreach (AssemblyName name in Assembly.Load(assemblyName).GetReferencedAssemblies())

  41:              GetReferencedAssemblyTree(ref assemblyNames, name);

  42:   

  43:      }

  44:   

  45:      private static Dictionary<string, List<string>> GetTypesInAssemblies(List<string> assemblies)

  46:      {

  47:          Dictionary<string, List<string>> coll = new Dictionary<string, List<string>>();

  48:   

  49:          foreach (string assemblyName in assemblies)

  50:          {

  51:              foreach (Type t in Assembly.Load(assemblyName).GetTypes())

  52:              {

  53:                  if (!coll.ContainsKey(t.Name))

  54:                      coll.Add(t.Name, new List<string>());

  55:                  coll[t.Name].Add(string.Format("{0} (in {1})", t.FullName, t.Assembly.GetName().Name));

  56:              }

  57:          }

  58:   

  59:          return coll;

  60:      }

  61:   

  62:      public static string FindInstancesOfClassName(string className)

  63:      {

  64:          StringBuilder result = new StringBuilder();

  65:          List<string> assemblies = new List<string>();

  66:          GetReferencedAssemblyTree(ref assemblies, null);

  67:          Dictionary<string, List<string>> coll = GetTypesInAssemblies(assemblies);

  68:          if (coll.ContainsKey(className))

  69:          {

  70:              result.AppendLine(string.Format("{0} implementations of a class named {1} exist:", coll[className].Count, className));

  71:   

  72:              foreach (string value in coll[className])

  73:                  result.AppendLine(string.Format("   {0}", value));

  74:          }

  75:          return result.ToString();

  76:      }

  77:  }



Just call something like the following:


Console.WriteLine(DuplicateClassFinder.FindInstancesOfClassName("Timer"));


And pass the name of the suspect class.