【问题标题】:Making extension methods from a third party library obsolete废弃第三方库的扩展方法
【发布时间】:2018-01-12 11:54:03
【问题描述】:

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

我使用包含对象扩展方法的第 3 方库。这会导致混乱,并可能在未来引起问题。 有没有办法在外部将此扩展方法(或某个 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 或命名空间
  • 链接的问题真的是重复的吗?这个问题是关于在你控制的 own 程序集中标记一个方法。这个问题是关于在第 3 方大会中标记的,你无法控制。我已经投票决定重新开放。
  • 最好的方法是使用Live Code Analyzer。不幸的是,创建它们并非易事,尽管这将是更简单的一种。
  • 我决定利用这个机会尝试创建我的第一个实时代码分析器。它进展...不太顺利,但我会在完成后为您发布代码。你想阻塞的方法的全名(包括命名空间、类和方法名)是什么?

标签: c# .net dll extension-methods


【解决方案1】:

您可以使用 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);
            }
        }
    }
}

有 3 个选项可以像这样使用分析器:

  • DiagnosticAnalyzer 代码直接添加到您的项目中。它仅适用于该解决方案。
  • 创建一个 包含DiagnosticAnalyzer 的类库,并分发它 作为 Nuget 包。它仅适用于使用该软件包的解决方案。
  • 编译包含 班级。分析器将适用于您加载的任何解决方案。

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

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

很多信息来自this tutorial,以及大量的反复试验。

【讨论】:

  • 如果您可以访问 Pluralsight,他们有一个非常好的课程,名为 .NET 编译器平台简介。它在这里和那里有点过时了,但它并没有降低它的用处。
  • 您可能要补充一点,如果严格禁止使用该方法,可以更改严重性以使代码不可编译。
【解决方案2】:

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

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

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

下面的代码示例引用了一个库,该库具有在 External 命名空间中定义的 ExtensionMethods 类。在标有(*) 注释的行中,该方法使用静态方法调用语法调用,编译器警告您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;
        }
    }
}

【讨论】:

    猜你喜欢
    • 2018-06-21
    • 2017-06-22
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 2018-03-11
    相关资源
    最近更新 更多