Cảnh báo trình biên dịch tùy chỉnh


115

Khi sử dụng ObsoleteAtribution trong .Net, nó cung cấp cho bạn các cảnh báo trình biên dịch cho bạn biết rằng đối tượng / phương thức / thuộc tính đã lỗi thời và nên sử dụng một cái gì đó khác. Tôi hiện đang làm việc trong một dự án đòi hỏi nhiều cấu trúc lại mã nhân viên cũ. Tôi muốn viết một thuộc tính tùy chỉnh mà tôi có thể sử dụng để đánh dấu các phương thức hoặc thuộc tính sẽ tạo ra các cảnh báo trình biên dịch đưa ra các thông báo mà tôi viết. Một cái gì đó như thế này

[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

Tôi muốn điều này tạo ra một cảnh báo trình biên dịch có nội dung: "Mã này sux và nên được xem xét". Tôi biết cách tạo thuộc tính tùy chỉnh, câu hỏi là làm thế nào để tôi tạo ra cảnh báo trình biên dịch trong phòng thu trực quan.


Đây có phải là C # không? Tôi sẽ giả định lại điều này là C # (không phải C) với giả định đó là những gì người đăng ban đầu muốn chọn.
Onorio Catenacci

13
Đó không phải là VB hoặc C # hợp lệ ... vậy nó là gì ...?!
ljs

5
Câu hỏi cũ, nhưng bạn có thể xác định cảnh báo trình biên dịch tùy chỉnh bằng Roslyn ngay bây giờ.
RJ Cuthbertson

4
@jrummell Trong Roslyn speak, máy phân tích mã: johnkoerner.com/csharp/creating-your-first-code-analyzer
RJ Cuthbertson

2
@RJCuthbertson Tôi đã chuyển nhận xét của bạn vào câu trả lời được chấp nhận để cung cấp cho nó sự chú ý mà nó xứng đáng.
jpaugh

Câu trả lời:


27

Cập nhật

Điều này hiện có thể với Roslyn (Visual Studio 2015). Bạn có thể xây dựng một bộ phân tích mã để kiểm tra một thuộc tính tùy chỉnh


Tôi không tin nó có thể. ObsoleteAttribution được xử lý đặc biệt bởi trình biên dịch và được định nghĩa trong tiêu chuẩn C #. Tại sao trên trái đất là ObsoleteAttribution không được chấp nhận? Dường như với tôi như thế này chính xác là tình huống mà nó được thiết kế và đạt được chính xác những gì bạn yêu cầu!

Cũng lưu ý rằng Visual Studio tiếp nhận các cảnh báo được tạo bởi ObsoleteAttribution khi đang di chuyển, điều này rất hữu ích.

Đừng có ích gì, chỉ tự hỏi tại sao bạn không thích sử dụng nó ...

Thật không may, ObsoleteAttribution bị niêm phong (có thể một phần do cách xử lý đặc biệt) do đó bạn không thể phân lớp thuộc tính của riêng bạn từ nó.

Từ tiêu chuẩn C #: -

Thuộc tính Lỗi thời được sử dụng để đánh dấu các loại và thành viên của các loại không còn được sử dụng.

Nếu một chương trình sử dụng một loại hoặc thành viên được trang trí bằng thuộc tính Lỗi thời, trình biên dịch sẽ đưa ra cảnh báo hoặc lỗi. Cụ thể, trình biên dịch đưa ra cảnh báo nếu không có tham số lỗi nào được cung cấp hoặc nếu tham số lỗi được cung cấp và có giá trị sai. Trình biên dịch sẽ báo lỗi nếu tham số lỗi được chỉ định và có giá trị đúng.

Không phải điều đó tổng hợp nhu cầu của bạn sao? ... bạn sẽ không làm tốt hơn những gì tôi không nghĩ.


14
Tôi đang tìm kiếm điều tương tự. Lỗi thời 'hoạt động' nhưng mã không thực sự lỗi thời đến mức không đầy đủ do tái cấu trúc.
g.

11
Tôi đồng ý với @g, và có lẽ là tác giả gốc. Lỗi thời có nghĩa là lỗi thời, không sử dụng. Tôi muốn gắn cờ một cái gì đó là "hey này biên dịch nhưng chúng tôi thực sự cần phải hoàn thành chức năng hoặc b) tái cấu trúc". Nó sẽ là một thuộc tính thời gian phát triển. Ngoài ra các tác vụ cũng hoạt động, ví dụ // TODO:, nhưng tôi không sử dụng chúng, vì tôi đoán nhiều người không làm, nhưng thường xuyên xem lại các cảnh báo của trình biên dịch.
MikeJansen

8
Một lý do khác để không sử dụng [Obsolete]thẻ là có thể gây ra vấn đề nếu bạn cần thực hiện XmlSerialization với thuộc tính. Thêm [Obsolete]thẻ cũng thêm [XmlIgnore]thuộc tính đằng sau hậu trường.
burnttoast11

6
Lỗi thời là khác Lỗi thời sẽ đưa ra cảnh báo cho bạn về mọi dòng mã gọi phương thức đó. Tôi không nghĩ đó là những gì người đăng muốn (ít nhất đó không phải là điều tôi muốn khi tôi thực hiện tìm kiếm và tìm thấy câu hỏi này). Tôi nghĩ những gì câu hỏi đang tìm kiếm là một cảnh báo để hiển thị về định nghĩa của chức năng, không bao giờ được sử dụng.
Nick

Không phải là câu trả lời lớn nhất. -1 vì nghĩ rằng bạn không có khả năng đưa ra lý do không sử dụng nó đáng bị chỉ trích. Thái độ này không khuyến khích tính xác thực.
Mike Socha III

96

Đây là giá trị một thử.

Bạn không thể mở rộng Lỗi thời, vì nó là cuối cùng, nhưng có lẽ bạn có thể tạo thuộc tính của riêng mình và đánh dấu lớp đó là lỗi thời như thế này:

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

Sau đó, khi bạn đánh dấu các phương thức của mình bằng thuộc tính "MustRefactor", các cảnh báo biên dịch sẽ hiển thị. Nó tạo ra một cảnh báo thời gian biên dịch, nhưng thông báo lỗi trông buồn cười, bạn nên tự mình xem nó và chọn. Điều này rất gần với những gì bạn muốn đạt được.

CẬP NHẬT: Với mã này Nó tạo ra một cảnh báo (không hay lắm, nhưng tôi không nghĩ có gì đó tốt hơn).

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
{

}

Bạn có thể dán vào những gì nó tạo ra? Tôi tò mò.
Micah

1
Cảnh báo biên dịch được kích hoạt ngay cả khi Thuộc tính / Phương thức không được gọi.
Rolf Kristensen

1
Gợi ý tốt ở đây. Tôi đang tìm cách làm điều tương tự, và cuối cùng chỉ ném NotImcellenceedExceptions. Không phải là giải pháp tốt nhất vì chúng không hiển thị vào thời gian biên dịch, chỉ trong thời gian chạy nếu mã xảy ra được thực thi. Tôi sẽ tự thử.
MonkeyWbler

1
Sẽ không tuyệt sao nếu ObsolteAttribution có thể hỗ trợ các biểu thức giống như DebuggerDisplayAttribution, sau đó chúng ta thực sự có thể làm một số thứ hay ho. visualstudio.uservoice.com/forums/121579-visual-studio/...
jpierson

Nếu bạn thực hiện IDisposabletrên các lớp lỗi thời đó, điều đó có nghĩa là bạn có thể bọc mã kiểm tra tinh ranh của mình trong một usingkhối. Như thế này : using(new MustRefactor()){DodgyCode();}. Sau đó, bạn có thể tìm thấy tất cả các công dụng khi bạn hoàn thành. Tôi đang sử dụng điều này ngay bây giờ cho Sleepchuỗi trong vòng lặp for Tôi cần làm chậm một cách giả tạo cho mục đích gỡ lỗi.
Iain Fraser

48

Trong một số trình biên dịch, bạn có thể sử dụng #warning để đưa ra cảnh báo:

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

Trong trình biên dịch Microsoft, bạn thường có thể sử dụng pragma thông báo:

#pragma message ( "text" )

Bạn đã đề cập đến .Net, nhưng không chỉ định liệu bạn đang lập trình với C / C ++ hay C #. Nếu bạn đang lập trình bằng C #, bạn nên biết rằng C # hỗ trợ định dạng #warning .


1
#warning hoặc #pragma là các chỉ thị tiền xử lý và do đó sẽ chạy bất kể sự hiện diện của bất kỳ mã đồng nghiệp cũ nào của micah và hoàn toàn không tương tác với thuộc tính. Khá chắc chắn Lỗi thời là phương tiện duy nhất để đạt được điều này ...
ljs

39

Chúng tôi hiện đang ở giữa rất nhiều lần tái cấu trúc, nơi chúng tôi không thể sửa chữa mọi thứ ngay lập tức. Chúng tôi chỉ sử dụng lệnh #proning preproc nơi chúng tôi cần quay lại và xem mã. Nó hiển thị trong đầu ra trình biên dịch. Tôi không nghĩ rằng bạn có thể đưa nó vào một phương thức, nhưng bạn có thể đặt nó vào bên trong phương thức đó và nó vẫn dễ tìm.

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

7

Trong VS 2008 (+ sp1) #warnings không hiển thị chính xác trong Danh sách lỗi sau Giải pháp tái tạo và tái tạo linh hồn sạch, không có tất cả chúng. Một số Cảnh báo được hiển thị trong Danh sách Lỗi chỉ sau khi tôi mở tệp lớp cụ thể. Vì vậy, tôi buộc phải sử dụng thuộc tính tùy chỉnh:

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

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

    public MappingToDo()
    {}
}

Vì vậy, khi tôi gắn cờ một số mã với nó

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

Nó tạo ra các cảnh báo như thế này:

Namespace.MickingToDo đã lỗi thời: 'Ánh xạ tớiDo'.

Tôi không thể thay đổi văn bản cảnh báo, 'Một số bình luận' không hiển thị trong Danh sách lỗi. Nhưng nó sẽ nhảy đến vị trí thích hợp trong tập tin. Vì vậy, nếu bạn cần thay đổi các thông báo cảnh báo như vậy, hãy tạo các thuộc tính khác nhau.


6

Những gì bạn đang cố gắng làm là lạm dụng các thuộc tính. Thay vào đó hãy sử dụng Danh sách tác vụ của Visual Studio. Bạn có thể nhập một nhận xét trong mã của bạn như thế này:

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

Sau đó mở View / Nhiệm vụ danh sách từ menu. Danh sách nhiệm vụ có hai loại, tác vụ người dùng và Nhận xét. Chuyển sang Nhận xét và bạn sẽ thấy tất cả // Todo: của bạn ở đó. Nhấp đúp vào TODO sẽ chuyển đến nhận xét trong mã của bạn.

Al


1
tôi thấy đây là một giải pháp tốt hơn
Samuel

1
nếu bạn muốn đánh dấu một chức năng là "Không được gọi trong mã sản xuất" hoặc tương tự. Vì vậy, bạn muốn nó kích hoạt nếu một hàm hoặc lớp được gọi hoặc khởi tạo, nhưng không phải nếu nó chỉ được biên dịch.
Jesse Pepper

2

Tôi không nghĩ bạn có thể. Theo như tôi biết thì việc hỗ trợ cho ObsoleteAttribution về cơ bản được mã hóa vào trình biên dịch C #; bạn không thể làm bất cứ điều gì tương tự trực tiếp.

Những gì bạn có thể làm là sử dụng một tác vụ MSBuild (hoặc một sự kiện hậu xây dựng) để thực thi một công cụ tùy chỉnh chống lại tập hợp vừa biên dịch. Công cụ tùy chỉnh sẽ phản ánh tất cả các loại / phương thức trong tập hợp và sử dụng thuộc tính tùy chỉnh của bạn, tại thời điểm đó, nó có thể in ra TextWriters mặc định hoặc lỗi của System.Console.


2

Nhìn vào nguồn cho ObsoleteAttribution , có vẻ như nó không làm gì đặc biệt để tạo cảnh báo trình biên dịch, vì vậy tôi có xu hướng đi với @ technophile và nói rằng nó được mã hóa cứng vào trình biên dịch. Có lý do nào bạn không muốn chỉ sử dụng ObsoleteAttribution để tạo thông điệp cảnh báo của mình không?


Không có lý do cụ thể nào ngoài mã không nhất thiết là lỗi thời.
Mi-chê

1
Nó được chỉ định trong đặc tả C # khi được trình biên dịch xử lý đặc biệt, hãy xem câu trả lời của tôi :-). Micah - 'Thuộc tính Lỗi thời được sử dụng để đánh dấu các loại và thành viên của các loại không còn được sử dụng.' từ đặc điểm kỹ thuật. Điều đó có áp dụng không? ...
ljs

Chỉ cần ai đó tự hỏi, không có mã C # trong mã nguồn để làm điều này. referencesource.microsoft.com/#mscorlib/system/...
Paweł Mach

1

Có một số ý kiến ​​đề nghị chèn cảnh báo hoặc pragma. Lỗi thời hoạt động theo một cách rất khác! Đánh dấu chức năng của thư viện L đã lỗi thời, thông báo lỗi thời xuất hiện khi chương trình gọi chức năng ngay cả khi chương trình người gọi không có trong thư viện L. Cảnh báo tăng thông báo CHỈ khi L được biên dịch.


1

Dưới đây là Thực hiện Roslyn, vì vậy bạn có thể tạo các thuộc tính của riêng mình để đưa ra cảnh báo hoặc lỗi khi đang di chuyển.

Tôi đã tạo một loại thuộc tính Được gọi là IdeMessagethuộc tính tạo cảnh báo:

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

    public IDEMessageAttribute(string message);
}

Để thực hiện việc này, bạn cần cài đặt Roslyn SDK trước và bắt đầu dự án VSIX mới với bộ phân tích. Tôi đã bỏ qua một số phần ít liên quan như tin nhắn, bạn có thể tìm ra cách để làm điều đó. Trong máy phân tích của bạn, bạn làm điều này

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);
}

Không có CodeFixProvider cho việc này, bạn có thể xóa nó khỏi giải pháp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.