简体   繁体   English

使第三方库中的扩展方法过时

[英]Making extension methods from a third party library obsolete

This question is not about a method I can mark with [System.Obsolete]. 这个问题与我用[System.Obsolete]标记的方法无关 The method I wanna ignore is in a dll I don't have control over. 我想忽略的方法是在一个我无法控制的DLL中。

I use a 3rd party library which contains an extension method for objects. 我使用第三方库,其中包含对象的扩展方法。 This leads to confusion and may cause problems in the future. 这会导致混淆,并可能在将来引起问题。 Is there any way to mark this extension method (or all the extension methods from a certain dll ) as obsolete externally or prevent this extension method appearing in intellisense. 有没有办法将此扩展方法(或来自某个dll的所有扩展方法)标记为外部过时或阻止此扩展方法出现在intellisense中。 The problematic method is : 有问题的方法是:

    public static class ExtensionMethods
    {
      public static bool IsNumeric(this object obj)
      {
        if (obj == null)
          return false;
        return obj.GetType().IsPrimitive || obj is double || (obj is Decimal || obj is DateTime) || obj is TimeSpan;
      }
    }

The best way to handle this would be to use Roslyn and create your own code analyzer, or use an existing tool like FxCop. 处理此问题的最佳方法是使用Roslyn并创建自己的代码分析器,或使用现有的工具,如FxCop。

However, I found a very non-elegant workaround for this. 但是,我发现了一个非常优雅的解决方法。

In your project you can create a class with the same name as the referenced class, residing in an identical namespace, with the exact same method. 在您的项目中,您可以创建一个与引用类同名的类,使用完全相同的方法驻留在相同的命名空间中。 Now mark your method as obsolete. 现在将您的方法标记为已过时。

The below code sample has a reference to a library with an ExtensionMethods class which is defined in the External namespace. 下面的代码示例引用了一个带有ExtensionMethods类的库,该类在External名称空间中定义。 In the line marked with (*) comment, where the method is called using the static method call syntax, the compiler warns you that the type ExtensionMethods conflicts with an imported type. 在标有(*)注释的行中,使用静态方法调用语法调用方法时,编译器会警告您ExtensionMethods类型与导入的类型冲突。 It also tells you that the method is obsolete (since you have shadowed the imported type, it sees your definition). 它还告诉您该方法已过时(因为您已隐藏导入的类型,它会看到您的定义)。 So when you invoke the method, your code will run. 因此,当您调用该方法时,您的代码将会运行。 In the line marked with (**) comment, where the method is called using the extension method call syntax, the compiler says that the call is ambiguous and the code won't compile. 在标有(**)注释的行中,使用扩展方法调用语法调用该方法,编译器表示调用不明确且代码无法编译。 The only workaround I know of is to turn this call into line (*) , which will produce the obsolete warning. 我所知道的唯一解决方法是将此调用转换为行(*) ,这将产生过时的警告。

With this solution you will be able to call other extension methods from the referenced type if you use the extension method syntax, provided you don't have the same method defined in your class. 使用此解决方案,如果使用扩展方法语法,则可以从引用类型调用其他扩展方法,前提是您没有在类中定义相同的方法。

using System;
using External;

namespace Internal
{
    class Program
    {
        static void Main(string[] args)
        {
            ExtensionMethods.IsNumeric(new object()); // (*)
            new object().IsNumeric(); // (**)
        }
    }
}

namespace External
{
    public static class ExtensionMethods
    {
        [Obsolete]
        public static bool IsNumeric(this object o)
        {
            if (obj == null)
              return false;
            return obj.GetType().IsPrimitive || obj is double || (obj is Decimal || obj is DateTime) || obj is TimeSpan;
        }
    }
}

You can do this with a Roslyn Code Analyzer. 您可以使用Roslyn Code Analyzer执行此操作。 The following code will create a DiagnosticAnalyzer that will give a compiler warning if String.EndsWith() is used. 以下代码将创建一个DiagnosticAnalyzer ,如果使用String.EndsWith() ,它将给出编译器警告。

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ForbiddenMethodsAnalyzer : DiagnosticAnalyzer
{
    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("Forbidden",
                                                                                 "Don't use this method!",
                                                                                 "Use of the '{0}' method is not allowed",
                                                                                 "Forbidden.Stuff",
                                                                                 DiagnosticSeverity.Warning,
                                                                                 isEnabledByDefault: true,
                                                                                 description: "This method is forbidden");
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyzeSyntaxNode, SyntaxKind.InvocationExpression);
    }

    private static void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context)
    {
        var invocationExpression = (InvocationExpressionSyntax)context.Node;
        var memberAccessExpression = invocationExpression.Expression as MemberAccessExpressionSyntax;
        if (memberAccessExpression?.Name.ToString() == "EndsWith")
        {
            var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessExpression).Symbol as IMethodSymbol;
            var containingType = memberSymbol.ContainingType;
            if (containingType.ContainingNamespace.Name == "System" && containingType.Name == "String")
            {
                var diagnostic = Diagnostic.Create(Rule, invocationExpression.GetLocation(), memberAccessExpression.ToString());
                context.ReportDiagnostic(diagnostic);
            }
        }
    }
}

工具提示警告 错误列表警告

There are 3 options to use an Analyzer like this: 使用分析器有3个选项,如下所示:

  • Add the DiagnosticAnalyzer code directly to your project. DiagnosticAnalyzer代码直接添加到项目中。 It will apply only to that solution. 它仅适用于该解决方案。
  • Create a class library with the DiagnosticAnalyzer in it, and distribute it as a Nuget package. 创建一个包含DiagnosticAnalyzer类库,并将其作为Nuget包分发。 It will apply only to solutions that use the package. 它仅适用于使用该软件包的解决方案。
  • Compile a full VSIX extension containing the class. 编译包含该类的完整VSIX扩展。 The analyzer will work on any solution you load. 分析仪可以处理您加载的任何解决方案。

This is the first project I've done that uses the Roslyn Code Analysis functionality, so unfortunately I don't understand everything that is going on here. 这是我使用Roslyn Code Analysis功能完成的第一个项目,所以不幸的是我不明白这里发生的一切。 I started with the default Analyzer template and tried various methods, stepped through code, and looked at variables with the watch windows until I found the information I needed for this functionality. 我从默认的Analyzer模板开始,尝试了各种方法,逐步执行代码,并使用监视窗口查看变量,直到找到此功能所需的信息。

The basic process is to register a SyntaxNode Analysis function, filtered to expressions that invoke a method. 基本过程是注册一个SyntaxNode Analysis函数,过滤到调用方法的表达式。 Within that method I check to see if the Name of the MemberAccessExpressionSyntax being examined is "EndsWith". 在该方法中,我检查要检查的MemberAccessExpressionSyntaxName是否为“EndsWith”。 If it is, I get the ContainingType that the method is on, and check to see if it is on the String class in the System namespace. 如果是,我获取该方法所在的ContainingType ,并检查它是否在System命名空间中的String类上。 If it is, I create a Diagnostic instance from a DiagnosticDescriptor to tell the IDE where the problem is, and how much of a problem it represents (A warning in this case, I could make it a full Error if I wanted, which would prevent the code from compiling). 如果是,我从DiagnosticDescriptor创建一个Diagnostic实例,告诉IDE问题出在哪里,以及它代表了多少问题(在这种情况下的警告,如果我愿意,我可以使它成为一个完整的错误,这会阻止来自编译的代码)。 It is also possible to present the user with different options to automatically fix the error, but I haven't explored that yet. 也可以向用户提供不同的选项来自动修复错误,但我还没有探索过。

A lot of the information came from this tutorial , as well as a whole lot of trial and error. 本教程提供了大量信息,以及大量的试验和错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM