使第三方库的扩展方法变得过时
这个问题是而不是关于我可以用[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;
}
}
来处理这种情况的最好方法是使用罗斯林和创建自己的代码分析仪,或使用像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代码分析功能所做的第一个项目,所以很不幸我不明白这里发生的一切。我开始使用默认的分析模板,并尝试了各种方法,遍历代码,并使用监视窗口查看变量,直到找到所需的信息为止。
基本过程是注册一个SyntaxNode分析函数,过滤到调用方法的表达式。在该方法中,我检查MemberAccessExpressionSyntax
的Name
是否是“EndsWith”。如果是,则获取方法处于打开状态的ContainingType
,然后检查它是否位于System
命名空间的String
类中。如果是这样,我从DiagnosticDescriptor
创建一个Diagnostic
实例来告诉IDE问题出在哪里以及它代表了多少问题(在这种情况下有一个警告,如果我愿意,可以将它设置为一个完整的错误,这将防止来自编译的代码)。也可以向用户提供不同的选项来自动修复错误,但我还没有探究过。
很多信息来自this tutorial,以及大量的试验和错误。
它是否在您可以安全忽略的名称空间中? –
@ DanielA.White是的,但我只想忽略扩展方法,不是整个DLL或命名空间 – yey
链接的问题是否真的重复?这个问题是关于在你自己的程序集中标记一个你控制的方法。这个问题是关于在第三方程序集中标记的,你无法控制。我已投票重新开放。 –