自定义编译器警告

中使用 ObsoleteAttribute 时。Net 会给出编译器警告,告诉您对象/方法/属性已经过时,应该使用其他内容。我目前正在做一个项目,需要对前雇员的代码进行大量的重构。我想写一个自定义属性,我可以使用它来标记方法或属性,这些方法或属性将生成给出我写的消息的编译器警告。就像这样

[MyAttribute("This code sux and should be looked at")]
public void DoEverything()
{
}
<MyAttribute("This code sux and should be looked at")>
Public Sub DoEverything()
End Sub

我希望这个代码生成一个编译器警告,说: “这段代码很糟糕,应该看一下”。我知道如何创建一个自定义属性,问题是如何使它在 Visual Studio 中生成编译器警告。

51359 次浏览

在一些编译器中,你可以使用 # 警告来发出警告:

#warning "Do not use ABC, which is deprecated. Use XYZ instead."

在 Microsoft 编译器中,通常可以使用消息杂注:

#pragma message ( "text" )

你提到过。但是没有具体说明你是用 C/C + + 还是 C # 编程。如果您使用 C # 编程,那么您应该知道 C # 支持 # 警告格式

我觉得你做不到。据我所知,对 ObsoleteAttribute 的支持基本上是硬编码到 C # 编译器中的; 您不能直接进行类似的操作。

您可以使用 MSBuild 任务(或生成后事件)对刚编译的程序集执行自定义工具。自定义工具将反映程序集中的所有类型/方法,并使用自定义属性,此时它可以打印到 System。控制台的默认值或错误 TextWriters。

这值得一试。

你不能扩展过时类,因为它是 final 类,但是你可以创建你自己的属性,然后像下面这样标记过时类:

[Obsolete("Should be refactored")]
public class MustRefactor: System.Attribute{}

然后,当您使用“ MustRefactor”属性标记方法时,将显示编译警告。它会生成一个编译时警告,但是错误消息看起来很有趣,您应该自己看看并选择。这和你想要达到的目标非常接近。

更新: 使用 这个密码,它会生成一个警告(不是很好,但我不认为有什么更好的东西)

public class User
{
private String userName;


[TooManyArgs] // Will show warning: Try removing some arguments
public User(String userName)
{
this.userName = userName;
}


public String UserName
{
get { return userName; }
}
[MustRefactor] // will show warning: Refactor is needed Here
public override string ToString()
{
return "User: " + userName;
}
}
[Obsolete("Refactor is needed Here")]
public class MustRefactor : System.Attribute
{


}
[Obsolete("Try removing some arguments")]
public class TooManyArgs : System.Attribute
{


}

看看 废弃属性的源代码,它看起来没有做任何特殊的事情来生成编译器警告,所以我倾向于使用@技术爱好者,并且说它是硬编码到编译器中的。是否有一个原因,您不想仅仅使用 废弃属性来生成您的警告消息?

更新

现在可以使用 Roslyn (VisualStudio2015)做到这一点。您可以使用 建造 a 代码分析器来检查自定义属性


原来过时的答案:

我觉得这不可能。ObsoleteAttribute 由编译器专门处理,并在 C # 标准中定义。到底为什么 ObsoleteAttribute 不可接受?在我看来,这正是它设计的目的所在,并且完全达到了你的要求!

还要注意的是,VisualStudio 也可以动态获取由 ObsoleteAttribute 生成的警告,这非常有用。

不是我不帮忙,只是想知道你为什么不喜欢用它..。

遗憾的是 ObsoleteAttribute 是密封的(可能部分原因是特殊处理) ,因此您不能从它继承您自己的属性。

来自 C # 标准:-

属性“过时”用于标记 类型的类型和成员 不再使用。

如果程序使用类型或成员 装饰着过时的 属性,则编译器发出一个 警告或错误 如果没有错误,编译器会发出警告 参数,或者如果错误 参数,并且具有 值 false。编译器发出一个 如果错误参数为 指定并具有值 true。

这不正是你所需要的吗? ... 你不会做得比这更好,我不认为。

我们目前正在进行大量的重构工作,无法立即修复所有问题。我们只是使用 # waringpreproc 命令,在这里我们需要返回并查看代码。它显示在编译器输出中。我不认为你可以把它放在一个方法上,但是你可以把它放在方法里面,它仍然很容易找到。

public void DoEverything() {
#warning "This code sucks"
}

在 VS2008(+ sp1) # 中,在清理解决方案和重建解决方案之后,警告没有在错误列表中正确显示,没有全部显示。 只有在我打开特定的类文件之后,一些警告才会显示在错误列表中。 所以我不得不使用自定义属性:

[Obsolete("Mapping ToDo")]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class MappingToDo : System.Attribute
{
public string Comment = "";


public MappingToDo(string comment)
{
Comment = comment;
}


public MappingToDo()
{}
}

所以当我用它标记一些代码时

[MappingToDo("Some comment")]
public class MembershipHour : Entity
{
// .....
}

它会产生这样的警告:

MappingToDo 已经过时: ‘ Mapping ToDo’。

我不能改变警告的文字,’一些评论’没有显示它错误列表。 但它会跳到适当的位置,在档案。 因此,如果您需要更改此类警告消息,请创建各种属性。

您正在尝试做的事情是对属性的滥用。而是使用 VisualStudio 任务列表。您可以在代码中输入如下注释:

//TODO:  This code sux and should be looked at
public class SuckyClass(){
//TODO:  Do something really sucky here!
}

然后从菜单中打开“视图/任务列表”。任务列表有两个类别: 用户任务和注释。切换到注释,你会看到你所有的//Todo: 的存在。双击 TODO 将跳转到代码中的注释。

艾尔

有几个注释建议插入警告或杂注。 过时的工作方式完全不同! 标记过时的库 L 函数,过时的消息在程序调用函数时引发,即使调用程序不在库 L 中。警告仅在编译 L 时引发消息。

下面是 Roslyn 实现,因此您可以创建自己的属性,即时提供警告或错误。

我已经创建了一个名为 IdeMessage的属性,该属性将生成警告:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class IDEMessageAttribute : Attribute
{
public string Message;


public IDEMessageAttribute(string message);
}

为此,您需要首先安装 Roslyn SDK,并使用分析器启动一个新的 VSIX 项目。我已经省略了一些不太相关的部分,比如信息,你可以想办法做到这一点。在你的分析仪中你这样做

public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzerInvocation, SyntaxKind.InvocationExpression);
}


private static void AnalyzerInvocation(SyntaxNodeAnalysisContext context)
{
var invocation = (InvocationExpressionSyntax)context.Node;


var methodDeclaration = (context.SemanticModel.GetSymbolInfo(invocation, context.CancellationToken).Symbol as IMethodSymbol);


//There are several reason why this may be null e.g invoking a delegate
if (null == methodDeclaration)
{
return;
}


var methodAttributes = methodDeclaration.GetAttributes();
var attributeData = methodAttributes.FirstOrDefault(attr => IsIDEMessageAttribute(context.SemanticModel, attr, typeof(IDEMessageAttribute)));
if(null == attributeData)
{
return;
}


var message = GetMessage(attributeData);
var diagnostic = Diagnostic.Create(Rule, invocation.GetLocation(), methodDeclaration.Name, message);
context.ReportDiagnostic(diagnostic);
}


static bool IsIDEMessageAttribute(SemanticModel semanticModel, AttributeData attribute, Type desiredAttributeType)
{
var desiredTypeNamedSymbol = semanticModel.Compilation.GetTypeByMetadataName(desiredAttributeType.FullName);


var result = attribute.AttributeClass.Equals(desiredTypeNamedSymbol);
return result;
}


static string GetMessage(AttributeData attribute)
{
if (attribute.ConstructorArguments.Length < 1)
{
return "This method is obsolete";
}


return (attribute.ConstructorArguments[0].Value as string);
}

这里没有 CodeFixProvider,您可以从解决方案中删除它。