127

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

[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

我希望这会生成一个编译器警告,上面写着“此代码 sux 并且应该查看”。我知道如何创建自定义属性,问题是如何使其在 Visual Studio 中生成编译器警告。

4

10 回答 10

101

这值得一试。

您不能扩展 Obsolete,因为它是最终的,但也许您可以创建自己的属性,并将该类标记为已过时,如下所示:

[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
{

}
于 2008-09-30T18:09:30.640 回答
51

在某些编译器中,您可以使用 #warning 发出警告:

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

在 Microsoft 编译器中,您通常可以使用 message pragma:

#pragma message ( "text" )

您提到了 .Net,但没有说明您是使用 C/C++ 还是 C# 进行编程。如果您使用 C# 进行编程,那么您应该知道C# 支持 #warning 格式

于 2008-09-30T17:33:50.460 回答
42

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

public void DoEverything() {
   #warning "This code sucks"
}
于 2008-09-30T19:39:32.093 回答
30

更新

现在可以使用 Roslyn (Visual Studio 2015)。您可以构建代码分析器来检查自定义属性


我不相信这是可能的。ObsoleteAttribute 由编译器特殊处理,并在 C# 标准中定义。为什么 ObsoleteAttribute 到底是不可接受的?在我看来,这正是它的设计目的,并准确地达到了你的要求!

另请注意,Visual Studio 也会即时获取 ObsoleteAttribute 生成的警告,这非常有用。

不要想无益,只是想知道为什么你不热衷于使用它......

不幸的是 ObsoleteAttribute 是密封的(可能部分是由于特殊处理),因此您不能从中继承您自己的属性。

从 C# 标准:-

属性 Obsolete 用于标记不应再使用的类型和类型的成员。

如果程序使用带有 Obsolete 属性的类型或成员,编译器会发出警告或错误。具体来说,如果没有提供错误参数,或者如果提供了错误参数并且值为 false,编译器会发出警告。如果指定了 error 参数并且值为 true,则编译器会发出错误。

这不是总结了您的需求吗?...您不会比我认为的做得更好。

于 2008-09-30T18:20:07.587 回答
8

在 VS 2008 (+sp1) 中,#warnings 在 Clean Soultion & Rebuild Solution 后没有正确显示在错误列表中,不是全部。只有在我打开特定的类文件后,错误列表中才会显示一些警告。所以我被迫使用自定义属性:

[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
{
    // .....
}

它会产生如下警告:

Namespace.MappingToDo 已过时:“映射 ToDo”。

我无法更改警告的文本​​,“一些评论”没有显示错误列表。但它会跳转到文件中的适当位置。因此,如果您需要更改此类警告消息,请创建各种属性。

于 2010-01-10T22:02:36.813 回答
7

你试图做的是滥用属性。而是使用 Visual Studio 任务列表。您可以像这样在代码中输入注释:

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

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

于 2014-09-28T21:37:20.770 回答
2

我不认为你可以。据我所知,对 ObsoleteAttribute 的支持基本上是硬编码到 C# 编译器中的;你不能直接做任何类似的事情。

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

于 2008-09-30T17:59:10.930 回答
2

查看ObsoleteAttribute的源代码,它看起来并没有做任何特别的事情来生成编译器警告,所以我倾向于使用@technophile并说它是硬编码到编译器中的。您是否有理由不想只使用ObsoleteAttribute来生成警告消息?

于 2008-09-30T18:11:35.470 回答
2

这是 Roslyn 实现,因此您可以创建自己的属性来即时发出警告或错误。

我创建了一个名为 Type CalledIdeMessage的属性,它将是生成警告的属性:

[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,您可以将其从解决方案中删除。

于 2017-07-31T15:10:13.580 回答
1

有几条评论建议插入警告或编译指示。过时的工作方式非常不同!标记库 L 的过时函数,即使调用程序不在库 L 中,程序调用该函数时也会引发过时消息。警告仅在编译 L 时引发消息。

于 2014-11-12T17:00:28.830 回答