将第三方库中的扩展方法变为过时的方法

6

这个问题并不是关于我可以用[System.Obsolete]标记的方法。我想忽略的方法在一个我无法控制的dll文件中。

我使用一个第三方库,其中包含一个用于对象的扩展方法。这会导致混淆,并可能在未来引起问题。是否有任何外部方法可以将此扩展方法(或来自某个特定dll的所有扩展方法)标记为过时,或防止此扩展方法出现在智能提示中。有问题的方法是:

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

@DanielA.White 是的,但我只想忽略扩展方法,而不是整个dll或命名空间。 - yey
1
链接的这个问题真的是一个重复的吗?那个问题是关于标记你自己控制的程序集中的方法。而这个问题则是关于标记第三方程序集中的方法,而你无法控制它们。我已经投票重新开放了。 - Bradley Uffner
最好的做法是使用Live Code Analyzer来完成。不幸的是,它们难以创建,即使这是其中比较简单的一种。 - Bradley Uffner
@BradleyUffner 是的,这不是重复的。谢谢。 - yey
1
我决定利用这个机会尝试创建我的第一个实时代码分析器。进展并不顺利,但是一旦完成,我会为您发布代码。您想要阻止的方法的完整名称(包括命名空间、类和方法名)是什么? - Bradley Uffner
显示剩余3条评论
2个回答

6
您可以使用Roslyn代码分析器来实现此功能。以下代码将创建一个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);
            }
        }
    }
}

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

有三种使用这样的分析器的选项:

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

这是我做的第一个使用Roslyn代码分析功能的项目,所以不幸的是我并不明白这里发生了什么。我从默认的分析器模板开始尝试各种方法,通过代码步进和观察窗口查看变量,直到找到了我需要的功能信息。

基本过程是注册一个SyntaxNode分析函数,过滤调用方法的表达式。在该方法内部,我检查正在检查的MemberAccessExpressionSyntaxName是否为“EndsWith”。如果是,我获取该方法所在的ContainingType,并检查它是否在System命名空间中的String类上。如果是,我创建一个DiagnosticDescriptorDiagnostic实例,告诉IDE问题所在以及问题的严重程度(在这种情况下是一个警告,我也可以将其设置为完整的错误,这将防止代码编译)。还可以向用户提供不同的选项以自动修复错误,但我尚未探讨这一点。

很多信息都来自于这个教程,以及大量的试错。


如果您有Pluralsight的访问权限,他们有一门非常好的课程叫做介绍.NET编译器平台。它在某些方面可能有点过时,但这并不会使其变得无用。 - Kapol
你可能想要补充一下,如果严格禁止使用该方法,可以将其严重程度更改为使代码无法编译。 - Kapol

4
处理这个问题的最佳方式是使用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;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接