使来自第三方库的扩展方法过时
Making extension methods from a third party library obsolete
这个问题不是关于我可以用[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;
}
}
处理此问题的最佳方法是使用 Roslyn 并创建您自己的代码分析器,或者使用 FxCop 等现有工具。
但是,我找到了一个非常不优雅的解决方法。
在您的项目中,您可以使用完全相同的方法创建一个与引用的 class 同名的 class,驻留在相同的命名空间中。现在将您的方法标记为已过时。
下面的代码示例引用了在 External
命名空间中定义的 ExtensionMethods
class 库。在标有 (*)
注释的行中,使用静态方法调用语法调用方法,编译器警告您类型 ExtensionMethods
与导入类型冲突。它还会告诉您该方法已过时(因为您隐藏了导入的类型,它会看到您的定义)。因此,当您调用该方法时,您的代码将 运行。在标有 (**)
注释的行中,使用扩展方法调用语法调用方法,编译器表示调用不明确,代码无法编译。我知道的唯一解决方法是将此调用转换为行 (*)
,这将产生过时的警告。
使用此解决方案,如果您使用扩展方法语法,您将能够从引用类型调用其他扩展方法,前提是您没有在 class.[=17 中定义相同的方法=]
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 代码分析器执行此操作。以下代码将创建一个 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
代码直接添加到您的项目中。它将仅适用于该解决方案。
- 创建一个
class 库,其中包含
DiagnosticAnalyzer
,并分发它
作为 Nuget 包。它将仅适用于使用该包的解决方案。
- 编译包含以下内容的完整 VSIX 扩展
class。分析器将适用于您加载的任何解决方案。
这是我完成的第一个使用 Roslyn 代码分析功能的项目,因此很遗憾,我不了解这里发生的一切。我从默认的 Analyzer 模板开始,尝试了各种方法,逐步执行代码,并使用手表 windows 查看变量,直到找到此功能所需的信息。
基本过程是注册一个 SyntaxNode 分析函数,过滤为调用方法的表达式。在该方法中,我检查正在检查的 MemberAccessExpressionSyntax
的 Name
是否为 "EndsWith"。如果是,我得到该方法打开的 ContainingType
,并检查它是否在 System
命名空间中的 String
class 上。如果是,我从 DiagnosticDescriptor
创建一个 Diagnostic
实例来告诉 IDE 问题出在哪里,以及它代表了多少问题(在这种情况下是一个警告,我可以如果我愿意,可以将其设置为完整错误,这将阻止代码编译)。也可以向用户提供不同的选项来自动修复错误,但我还没有探索过。
很多信息来自 this tutorial,以及大量的试验和错误。
这个问题不是关于我可以用[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;
}
}
处理此问题的最佳方法是使用 Roslyn 并创建您自己的代码分析器,或者使用 FxCop 等现有工具。
但是,我找到了一个非常不优雅的解决方法。
在您的项目中,您可以使用完全相同的方法创建一个与引用的 class 同名的 class,驻留在相同的命名空间中。现在将您的方法标记为已过时。
下面的代码示例引用了在 External
命名空间中定义的 ExtensionMethods
class 库。在标有 (*)
注释的行中,使用静态方法调用语法调用方法,编译器警告您类型 ExtensionMethods
与导入类型冲突。它还会告诉您该方法已过时(因为您隐藏了导入的类型,它会看到您的定义)。因此,当您调用该方法时,您的代码将 运行。在标有 (**)
注释的行中,使用扩展方法调用语法调用方法,编译器表示调用不明确,代码无法编译。我知道的唯一解决方法是将此调用转换为行 (*)
,这将产生过时的警告。
使用此解决方案,如果您使用扩展方法语法,您将能够从引用类型调用其他扩展方法,前提是您没有在 class.[=17 中定义相同的方法=]
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 代码分析器执行此操作。以下代码将创建一个 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
代码直接添加到您的项目中。它将仅适用于该解决方案。 - 创建一个
class 库,其中包含
DiagnosticAnalyzer
,并分发它 作为 Nuget 包。它将仅适用于使用该包的解决方案。 - 编译包含以下内容的完整 VSIX 扩展 class。分析器将适用于您加载的任何解决方案。
这是我完成的第一个使用 Roslyn 代码分析功能的项目,因此很遗憾,我不了解这里发生的一切。我从默认的 Analyzer 模板开始,尝试了各种方法,逐步执行代码,并使用手表 windows 查看变量,直到找到此功能所需的信息。
基本过程是注册一个 SyntaxNode 分析函数,过滤为调用方法的表达式。在该方法中,我检查正在检查的 MemberAccessExpressionSyntax
的 Name
是否为 "EndsWith"。如果是,我得到该方法打开的 ContainingType
,并检查它是否在 System
命名空间中的 String
class 上。如果是,我从 DiagnosticDescriptor
创建一个 Diagnostic
实例来告诉 IDE 问题出在哪里,以及它代表了多少问题(在这种情况下是一个警告,我可以如果我愿意,可以将其设置为完整错误,这将阻止代码编译)。也可以向用户提供不同的选项来自动修复错误,但我还没有探索过。
很多信息来自 this tutorial,以及大量的试验和错误。