不——这是扩展方法的一个已知缺点,需要非常小心。就我个人而言,我希望 C# 编译器会警告您,如果您声明了一个扩展方法,该方法除了通过正常的静态路由之外永远不会被调用(ExtensionClassName.MethodName(target, ...)
).
编写一个小工具来检查程序集中的所有扩展方法并以这种方式发出警告可能不会太难。可能不需要very精确的开始:如果已经有一个同名的方法(不用担心参数类型),只是警告将是一个好的开始。
编辑:好的......这是一个非常粗糙工具至少给出一个起点。它似乎至少可以工作some泛型类型的范围 - 但它并没有尝试对参数类型或名称执行任何操作...部分原因是参数数组变得很棘手。它还“完全”加载程序集,而不是仅通过反射加载程序集,这会更好 - 我尝试了“正确的”路线,但遇到了一些无法立即解决的问题,因此又回到了快速而肮脏的路线: )
无论如何,希望它对某人、某处有用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
public class ExtensionCollisionDetector
{
private static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine
("Usage: ExtensionCollisionDetector <assembly file> [...]");
return;
}
foreach (string file in args)
{
Console.WriteLine("Testing {0}...", file);
DetectCollisions(file);
}
}
private static void DetectCollisions(string file)
{
try
{
Assembly assembly = Assembly.LoadFrom(file);
foreach (var method in FindExtensionMethods(assembly))
{
DetectCollisions(method);
}
}
catch (Exception e)
{
// Yes, I know catching exception is generally bad. But hey,
// "something's" gone wrong. It's not going to do any harm to
// just go onto the next file.
Console.WriteLine("Error detecting collisions: {0}", e.Message);
}
}
private static IEnumerable<MethodBase> FindExtensionMethods
(Assembly assembly)
{
return from type in assembly.GetTypes()
from method in type.GetMethods(BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic)
where method.IsDefined(typeof(ExtensionAttribute), false)
select method;
}
private static void DetectCollisions(MethodBase method)
{
Console.WriteLine(" Testing {0}.{1}",
method.DeclaringType.Name, method.Name);
Type extendedType = method.GetParameters()[0].ParameterType;
foreach (var type in GetTypeAndAncestors(extendedType).Distinct())
{
foreach (var collision in DetectCollidingMethods(method, type))
{
Console.WriteLine(" Possible collision in {0}: {1}",
collision.DeclaringType.Name, collision);
}
}
}
private static IEnumerable<Type> GetTypeAndAncestors(Type type)
{
yield return type;
if (type.BaseType != null)
{
// I want yield foreach!
foreach (var t in GetTypeAndAncestors(type.BaseType))
{
yield return t;
}
}
foreach (var t in type.GetInterfaces()
.SelectMany(iface => GetTypeAndAncestors(iface)))
{
yield return t;
}
}
private static IEnumerable<MethodBase>
DetectCollidingMethods(MethodBase extensionMethod, Type type)
{
// Very, very crude to start with
return type.GetMethods(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic)
.Where(candidate => candidate.Name == extensionMethod.Name);
}
}