Спеціальні попередження компілятора


115

Під час використання ObsoleteAtribute в .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

Я хочу, щоб це генерувало попередження компілятора, яке говорить: "Цей код сухий і його слід переглянути". Я знаю, як створити спеціальний атрибут, питання полягає в тому, як змусити його генерувати попередження компілятора у візуальній студії.


Це C #? Я маю на меті припустимо позначити це як C # (а не C) на презумпції, саме так мав вибрати оригінальний плакат.
Оноріо Катенац

13
Це не вірно VB чи C # ... так що це ...?!
ljs

5
Старе питання, але ви можете визначити спеціальні попередження компілятора, використовуючи Roslyn зараз.
RJ Cuthbertson

4
@jrummell В розмові Рослин аналізатори коду: johnkoerner.com/csharp/creating-your-first-code-analyzer
RJ Cuthbertson

2
@RJCuthbertson Я перемістив ваш коментар у прийняту відповідь, щоб приділити йому увагу, яке воно заслуговує.
jpaugh

Відповіді:


27

Оновлення

Зараз це можливо за допомогою Roslyn (Visual Studio 2015). Ви можете побудувати на аналізатор коду для перевірки користувальницького атрибута


Я не вірю, що це можливо. ObsoleteAttribute обробляється спеціально компілятором і визначається стандартом C #. Чому на Землі застарілий розподіл неприйнятний? Мені здається, що це саме та ситуація, для якої вона була розроблена, і досягає саме того, що вам потрібно!

Також зауважте, що Visual Studio також піднімає попередження, створені ObsoleteAttribute на льоту, що дуже корисно.

Не майте на увазі бути непосидними, просто цікавіться, чому ви не прагнете його використовувати ...

На жаль, ObsoleteAttribute запечатаний (можливо, частково завдяки спеціальній обробці), отже, ви не можете підкласифікувати свій власний атрибут.

Зі стандарту C #: -

Атрибут Застарілий використовується для позначення типів та членів типів, які більше не слід використовувати.

Якщо програма використовує тип або член, який прикрашений атрибутом "Застаріле", компілятор видає попередження або помилку. Зокрема, компілятор видає попередження, якщо не вказаний параметр помилки або якщо параметр помилки надано та має значення false. Компілятор видає помилку, якщо вказаний параметр помилки і має значення true.

Чи це не підсумовує ваші потреби? ... Ви не збираєтеся робити краще, ніж це, я не думаю.


14
Я шукаю те саме. Застарілий "працює", але код насправді не є застарілим настільки неповним через рефакторинг.
г.

11
Я згоден з @g і, мабуть, оригінальним автором. Застарілий означає застарілий, не використовуйте. Я хочу позначити щось як "Ей це компілюється, але нам дійсно потрібно або: а) завершити функціональність або б) рефактор". Це було б більше атрибутом часу розвитку. Також працюють завдання, наприклад // TODO :, але я їх не використовую, як, мабуть, багато людей цього не роблять, але регулярно переглядайте попередження компілятора.
MikeJansen

8
Ще одна причина не використовувати [Obsolete]тег - це те, що може призвести до проблем, якщо вам потрібно виконати XmlSerialization з властивістю. Додавання [Obsolete]тегу також додає [XmlIgnore]атрибут за кадром.
burnttoast11

6
Застарілий буває різним. Застарілий видасть вам попередження про кожен рядок коду, що викликає цей метод. Я не думаю, що цього хоче плакат (принаймні, це не те, чого я хочу, коли я робив пошук і знайшов це питання). Я подумав, що запитання шукає - це попередження про появу визначення функції, а не про те, що вона використовується.
Нік

Не найбільша відповідь. -1 за те, що ви думаєте про вашу нездатність придумати причину її невикористання, заслуговує на критику. Таке ставлення відлякує справжність.
Майк Соха ІІІ

96

Це варто спробувати.

Ви не можете розширити застаріле, оскільки воно остаточне, але, можливо, ви можете створити власний атрибут та позначити цей клас як застарілий, як це:

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

}

Чи можете ви вставити те, що воно створює? Мені цікаво.
Міхей

1
Попередження про компіляцію спрацьовує, навіть якщо властивість / метод не викликається.
Рольф Крістенсен

1
Тут хороші пропозиції. Я хотів зробити те саме, і в кінцевому підсумку просто кинув NotImplementedExceptions. Не найкраще рішення, оскільки вони не з’являються під час компіляції, лише під час виконання, якщо код буде виконано. Я сам спробую це спробувати.
MonkeyWrench

1
Хіба це не було б здорово, якби ObsolteAttribute міг підтримувати вирази так само, як DebuggerDisplayAttribute, тоді ми дійсно могли б зробити якісь цікаві речі. visualstudio.uservoice.com/forums/121579-visual-studio/…
jpierson

Якщо ви реалізуєте IDisposableна цих застарілих класах, це означає, що ви зможете зафіксувати свій хиткий тестовий код у usingблоці. Як це: using(new MustRefactor()){DodgyCode();}. Тоді ви зможете знайти всі звичаї, коли закінчите. Я зараз використовую це для Sleepпотоку всередині циклу для циклу, який мені потрібно штучно сповільнити для налагодження.
Ієн Фрейзер

48

У деяких компіляторах ви можете використовувати #warning, щоб надіслати попередження:

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

У компіляторах Microsoft, як правило, можна використовувати прагму повідомлення:

#pragma message ( "text" )

Ви згадали .Net, але не вказали, чи програмували ви C / C ++ чи C #. Якщо ви програмуєте на C #, то ви повинні знати, що C # підтримує формат # попередження .


1
#warning або #pragma - це передпроцесорні директиви, і тому вони працюватимуть незалежно від наявності будь-якого коду колишніх колег Міхи, і він взагалі не взаємодіє з атрибутом. Досить певний застарілий - єдиний засіб для досягнення цього ...
ljs

39

Зараз ми перебуваємо в середині багато рефакторингу, де ми не змогли все виправити. Ми просто використовуємо команду #warning preproc, куди нам потрібно повернутися назад і подивитися на код. Це відображається у висновку компілятора. Я не думаю, що ви можете застосувати його до методу, але ви можете поставити його просто всередині методу, і це все ще легко знайти.

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

7

У VS 2008 (+ sp1) # попередження не відображаються належним чином у списку помилок після 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
{
    // .....
}

Він видає попередження таким чином:

Простір імен.MappingToDo є застарілим: 'Mapping ToDo'.

Я не можу змінити текст попередження: "Деякі коментарі" не відображається у списку помилок. Але він перейде на потрібне місце у файлі. Тож якщо вам потрібно змінити такі попереджувальні повідомлення, створіть різні атрибути.


6

Те, що ви намагаєтеся зробити, - це неправильне використання атрибутів. Замість цього використовуйте список завдань Visual Studio. Ви можете ввести коментар у свій код так:

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

Потім відкрийте Перегляд / Перелік завдань у меню. Список завдань містить дві категорії, завдання користувача та коментарі. Перейдіть до коментарів, і ви побачите всі ваші // Todo: там. Подвійне клацання на TODO перейде до коментаря у вашому коді.

Ал


1
Я вважаю це більш бажаним рішенням
Самуїл,

1
що робити, якщо ви хочете позначити функцію як "Не називатися у виробничому коді" або подібну. Таким чином, ви хочете, щоб він запускався, якщо функція або клас викликається або інстанціюється, але не, якщо він просто скомпільований.
Джессі Пеппер

2

Я не думаю, що ти можеш. Наскільки я знаю, підтримка ObsoleteAttribute по суті важко кодується в компілятор C #; не можна робити щось подібне безпосередньо.

Можливо, ви можете використовувати завдання MSBuild (або подію після складання), яка виконує спеціальний інструмент проти щойно складеної збірки. Спеціальний інструмент відображатиме всі типи / методи в складанні та споживає ваш спеціальний атрибут, і в цей момент він може друкувати до TextWriters за замовчуванням або помилку TextWriters.


2

Дивлячись на джерело для ObsoleteAttribute , це не схоже на те, що він робить щось особливе, щоб генерувати попередження про компілятор, тому я схильний би перейти з @ technophile і сказати, що це важко закодовано в компілятор. Чи є причина, що ви не хочете просто використовувати ObsoleteAttribute для створення попереджувальних повідомлень?


Жодна конкретна причина, крім коду, не обов'язково застаріла.
Міхей

1
У специфікації C # зазначено, що компілятор спеціально поводиться з ними, перевірте мою відповідь :-). Міка - "Атрибут Застарілий використовується для позначення типів та членів типів, які більше не слід використовувати." із специфікації. Це не застосовується? ...
ljs

Просто якщо хтось задумався, у вихідному коді немає жодного C # коду, щоб це зробити. referenceource.microsoft.com/#mscorlib/system/…
Paweł Mach

1

Є кілька коментарів, які пропонують вставити попередження або прагму. Застарілий працює зовсім по-іншому! Позначивши застарілу функцію бібліотеки L, застаріле повідомлення піднімається, коли програма викликає функцію, навіть якщо програма, що викликає абонента, не знаходиться в бібліотеці L. Попередження піднімає повідомлення ТІЛЬКИ, коли L компілюється.


1

Ось реалізація Roslyn, тож ви можете створювати свої власні атрибути, які дають застереження чи помилки під час руху.

Я створив атрибут Type Called, IdeMessageякий буде атрибутом, який генерує попередження:

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

    public IDEMessageAttribute(string message);
}

Для цього вам потрібно спочатку встановити пакет SDK Roslyn і запустити новий проект 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, ви можете видалити його з рішення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.