Domanda

Quando si utilizza ObsoleteAtribute in .Net, vengono visualizzati avvisi del compilatore che indicano che l'oggetto / metodo / proprietà sono obsoleti e che si dovrebbe utilizzare qualcos'altro. Attualmente sto lavorando a un progetto che richiede molto refactoring di un codice ex dipendenti. Voglio scrivere un attributo personalizzato che posso usare per contrassegnare metodi o proprietà che genereranno avvisi del compilatore che forniscono messaggi che scrivo. Qualcosa del genere

[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

Voglio che questo generi un avviso del compilatore che dice, "Questo codice sux e dovrebbe essere considerato". So come creare un attributo personalizzato, la domanda è: come faccio a generare avvisi di compilatore in Visual Studio.

È stato utile?

Soluzione

Aggiornamento

Questo è ora possibile con Roslyn (Visual Studio 2015). Puoi build a analizzatore di codice per verificare la presenza di un attributo personalizzato


Non credo sia possibile. ObsoleteAttribute è trattato appositamente dal compilatore ed è definito nello standard C #. Perché mai ObsoleteAttribute non è accettabile? Mi sembra che questa sia esattamente la situazione per cui è stata progettata e raggiunge esattamente ciò di cui hai bisogno!

Nota anche che Visual Studio raccoglie gli avvisi generati da ObsoleteAttribute anche al volo, il che è molto utile.

Non intendo essere inutile, mi chiedo solo perché non ti piace usarlo ...

Purtroppo ObsoleteAttribute è sigillato (probabilmente in parte a causa del trattamento speciale), quindi non puoi sottoclassare il tuo attributo da esso.

Dallo standard C #: -

  

L'attributo Obsoleto viene utilizzato per contrassegnare   tipi e membri di tipi che dovrebbero   non essere più utilizzato.

     

Se un programma utilizza un tipo o un membro   che è decorato con l'O obsoleto   attributo, il compilatore emette a   avviso o errore. In particolare, il   il compilatore emette un avviso se nessun errore   viene fornito il parametro o se l'errore   Il parametro viene fornito e ha il parametro   valore falso. Il compilatore rilascia un   errore se il parametro error è   specificato e ha il valore vero.

Non riassume le tue esigenze? ... non farai meglio di così non credo.

Altri suggerimenti

Non so se funzionerà, ma vale la pena provare.

Non puoi estendere Obsolete, perché è definitivo, ma forse puoi creare il tuo attributo e contrassegnare quella classe come obsoleta come questa:

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

Quindi, quando contrassegni i tuoi metodi con " MustRefactor " attributo, potrebbero essere visualizzati gli avvisi di compilazione.

Ho detto " forse " e "potrebbe" perché non l'ho provato. Per favore dimmi se non funziona quindi rimuoverò la risposta.

Saluti!

AGGIORNAMENTO: testato. Genera un avviso di compilazione, ma il messaggio di errore sembra divertente, dovresti vederlo da solo e scegliere. Questo è molto vicino a ciò che volevi ottenere.

UPDATE2: Con questo codice genera questo avvertimento (non molto bello, ma non credo che ci sia qualcosa di meglio).

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
{

}

In alcuni compilatori è possibile utilizzare #warning per emettere un avviso:

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

Nei compilatori Microsoft, in genere è possibile utilizzare il messaggio pragma:

#pragma message ( "text" )

Hai menzionato .Net, ma non hai specificato se stavi programmando con C / C ++ o C #. Se stai programmando in C #, dovresti sapere che C # supporta il formato #warning .

Attualmente siamo nel bel mezzo di molti refactoring in cui non siamo riusciti a risolvere tutto immediatamente. Usiamo semplicemente il comando #warning preproc dove dobbiamo tornare indietro e guardare il codice. Si presenta nell'output del compilatore. Non penso che tu possa metterlo su un metodo, ma potresti inserirlo proprio all'interno del metodo, ed è ancora facile da trovare.

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

In VS 2008 (+ sp1) #warnings non viene visualizzato correttamente nell'elenco degli errori dopo Clean Soultion & amp; Ricostruisci la soluzione, non tutte. Alcuni avvisi vengono visualizzati nell'elenco degli errori solo dopo aver aperto un determinato file di classe. Quindi sono stato costretto a utilizzare l'attributo personalizzato:

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

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

    public MappingToDo()
    {}
}

Quindi quando contrassegno del codice con esso

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

Produce avvisi come questo:

  

Namespace.MappingToDo è obsoleto:   "Mapping ToDo".

Non riesco a modificare il testo dell'avviso, "Alcuni commenti" non vengono mostrati Elenco errori. Ma salterà al posto giusto nel file. Pertanto, se è necessario modificare tali messaggi di avviso, creare vari attributi.

Quello che stai cercando di fare è un uso improprio degli attributi. Utilizzare invece l'Elenco attività di Visual Studio. Puoi inserire un commento nel tuo codice in questo modo:

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

Quindi aprire Visualizza / Elenco attività dal menu. L'elenco delle attività ha due categorie, attività dell'utente e commenti. Passa a Commenti e vedrai tutti i tuoi // Todo: è lì. Facendo doppio clic su un TODO salterà al commento nel tuo codice.

Al

Non penso che tu possa. Per quanto ne so, il supporto per ObsoleteAttribute è essenzialmente codificato nel compilatore C #; non puoi fare nulla di simile direttamente.

Quello che potresti essere in grado di fare è usare un'attività MSBuild (o un evento post-build) che esegue uno strumento personalizzato sull'assembly appena compilato. Lo strumento personalizzato rifletterebbe su tutti i tipi / metodi nell'assembly e consumerebbe l'attributo personalizzato, a quel punto potrebbe stampare su SystemWonsers predefinito o errore di System.Console.

Guardando la fonte per ObsoleteAttribute , non sembra che stia facendo qualcosa di speciale per generare un avviso del compilatore, quindi tenderei ad andare con @ technophile e dire che è codificato nel compilatore. C'è un motivo per cui non vuoi semplicemente usare ObsoleteAttribute per generare i tuoi messaggi di avviso?

Esistono diversi commenti che suggeriscono di inserire avvisi o pragma. Obsolete funziona in un modo molto diverso! Contrassegnando una funzione obsoleta di una libreria L, il messaggio obsoleto viene generato quando un programma chiama la funzione anche se il programma chiamante non si trova nella libreria L. L'avvertimento genera il messaggio SOLO quando viene compilato L.

Ecco l'implementazione di Roslyn, quindi puoi creare i tuoi attributi che danno avvisi o errori al volo.

Ho creato un attributo Tipo chiamato IdeMessage che sarà l'attributo che genera avvisi:

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

    public IDEMessageAttribute(string message);
}

Per fare ciò è necessario installare prima l'SDK Roslyn e avviare un nuovo progetto VSIX con l'analizzatore. Ho omesso alcuni dei pezzi meno rilevanti come i messaggi, puoi capire come farlo. Nel tuo analizzatore lo fai

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

Non ci sono CodeFixProvider per questo è possibile rimuoverlo dalla soluzione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top