繁体   English   中英

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

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

这个问题与我用[System.Obsolete]标记的方法无关 我想忽略的方法是在一个我无法控制的DLL中。

我使用第三方库,其中包含对象的扩展方法。 这会导致混淆,并可能在将来引起问题。 有没有办法将此扩展方法(或来自某个dll的所有扩展方法)标记为外部过时或阻止此扩展方法出现在intellisense中。 有问题的方法是:

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

处理此问题的最佳方法是使用Roslyn并创建自己的代码分析器,或使用现有的工具,如FxCop。

但是,我发现了一个非常优雅的解决方法。

在您的项目中,您可以创建一个与引用类同名的类,使用完全相同的方法驻留在相同的命名空间中。 现在将您的方法标记为已过时。

下面的代码示例引用了一个带有ExtensionMethods类的库,该类在External名称空间中定义。 在标有(*)注释的行中,使用静态方法调用语法调用方法时,编译器会警告您ExtensionMethods类型与导入的类型冲突。 它还告诉您该方法已过时(因为您已隐藏导入的类型,它会看到您的定义)。 因此,当您调用该方法时,您的代码将会运行。 在标有(**)注释的行中,使用扩展方法调用语法调用该方法,编译器表示调用不明确且代码无法编译。 我所知道的唯一解决方法是将此调用转换为行(*) ,这将产生过时的警告。

使用此解决方案,如果使用扩展方法语法,则可以从引用类型调用其他扩展方法,前提是您没有在类中定义相同的方法。

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

您可以使用Roslyn Code Analyzer执行此操作。 以下代码将创建一个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);
            }
        }
    }
}

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

使用分析器有3个选项,如下所示:

  • DiagnosticAnalyzer代码直接添加到项目中。 它仅适用于该解决方案。
  • 创建一个包含DiagnosticAnalyzer类库,并将其作为Nuget包分发。 它仅适用于使用该软件包的解决方案。
  • 编译包含该类的完整VSIX扩展。 分析仪可以处理您加载的任何解决方案。

这是我使用Roslyn Code Analysis功能完成的第一个项目,所以不幸的是我不明白这里发生的一切。 我从默认的Analyzer模板开始,尝试了各种方法,逐步执行代码,并使用监视窗口查看变量,直到找到此功能所需的信息。

基本过程是注册一个SyntaxNode Analysis函数,过滤到调用方法的表达式。 在该方法中,我检查要检查的MemberAccessExpressionSyntaxName是否为“EndsWith”。 如果是,我获取该方法所在的ContainingType ,并检查它是否在System命名空间中的String类上。 如果是,我从DiagnosticDescriptor创建一个Diagnostic实例,告诉IDE问题出在哪里,以及它代表了多少问题(在这种情况下的警告,如果我愿意,我可以使它成为一个完整的错误,这会阻止来自编译的代码)。 也可以向用户提供不同的选项来自动修复错误,但我还没有探索过。

本教程提供了大量信息,以及大量的试验和错误。

暂无
暂无

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

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