通过类覆盖扩展方法给出任何警告 [英] Extension methods overridden by class gives no warning

查看:175
本文介绍了通过类覆盖扩展方法给出任何警告的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在另一个线程的讨论,并且发现了类方法优先于扩展方法具有相同的名称和参数。这是好的推广方法不会劫持方法,但是假设你已经增加了一些扩展方法,第三方库:

I had a discussion in another thread, and found out that class methods takes precedence over extension methods with the same name and parameters. This is good as extension methods won't hijack methods, but assume you have added some extension methods to a third party library:

public class ThirdParty
{
}

public static class ThirdPartyExtensions
{
    public static void MyMethod(this ThirdParty test)
    {
        Console.WriteLine("My extension method");
    }
}



按预期工作:ThirdParty.MyMethod - >我的扩展方法

Works as expected: ThirdParty.MyMethod -> "My extension method"

但随后的第三方更新它的图书馆,准确地增加了一个方法,像你的扩展方法:

But then ThirdParty updates it's library and adds a method exactly like your extension method:

public class ThirdParty
{
    public void MyMethod()
    {
        Console.WriteLine("Third party method");
    }
}

public static class ThirdPartyExtensions
{
    public static void MyMethod(this ThirdParty test)
    {
        Console.WriteLine("My extension method");
    }
}



ThirdPart.MyMethod - >第三方方法

ThirdPart.MyMethod -> "Third party method"

现在突然代码的行为在运行时不同的第三方法劫持您的扩展方法!编译器不会给出任何警告。

Now suddenly code will behave different at runtime as the third party method has "hijacked" your extension method! The compiler doesn't give any warnings.

有没有一种方法,使这样的警告或以其他方式避免这种情况?

Is there a way to enable such warnings or otherwise avoid this?

推荐答案

都能跟得上 - 这是扩展方法已知的缺点,有什么东西要非常小心。我个人希望C#编译器会警告你,如果你宣布一个扩展方法,它永远不会调用不是通过正常的静态路由( ExtensionClassName.MethodName(目标,...))。

Nope - this is a known downside of extension methods, and something to be very careful about. Personally I wish that the C# compiler would warn you if you declared an extension method which would never be called other than via the normal static route (ExtensionClassName.MethodName(target, ...)).

这可能不会是太难写一个小工具来检查装配并发出警告,这种方式的所有扩展方法。它可能并不需要的非常的精确入手:只是警告,如果已经有同名的(不用担心参数类型)的方法将是一个良好的开端。

It probably wouldn't be too hard to write a little tool to examine all the extension methods in an assembly and issue warnings that way. It probably wouldn't need to be very precise to start with: just warning if there's already a method with the same name (without worrying about parameter types) would be a good start.

编辑:好的...这里是一个的很简陋的工具,至少给一个起点。这似乎至少要工作的部分的使用泛型类型程度 - 但它没有试图做参数类型或名称什么...部分原因是因为这变得棘手与参数数组。它还加载组件充分,而不是只用反射,这将是更好 - 我尝试了正确的路线,但遇到了一些问题,并没有立即小事来解决,所以回落到快速和肮脏的路线: )

Okay... here's a very crude tool to at least give a starting point. It appears to work at least to some extent with generic types - but it's not trying to do anything with parameter types or names... partly because that becomes tricky with parameter arrays. It also loads assemblies "fully" instead of with reflection only, which would be nicer - I tried the "proper" route, but ran into some problems which weren't immediately trivial to resolve, so fell back to the quick and dirty route :)

不管怎样,希望这将是有用的人,什么地方。

Anyway, hopefully it'll be useful to someone, somewhere.

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);
    }
}

这篇关于通过类覆盖扩展方法给出任何警告的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆