Pregunta

Cuando se usa el atributo ObsoleteAtribute en .Net, le proporciona advertencias de compilación que le indican que el objeto / método / propiedad está obsoleto y que se debe usar algo más. Actualmente estoy trabajando en un proyecto que requiere una gran cantidad de refactorización de un código de ex empleados. Quiero escribir un atributo personalizado que pueda usar para marcar métodos o propiedades que generarán advertencias del compilador que dan mensajes que escribo. Algo como esto

[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

Quiero que esto genere una advertencia del compilador que diga, " Este código sux y debería verse " ;. Sé cómo crear un atributo personalizado, la pregunta es cómo hacer que genere advertencias de compilación en Visual Studio.

¿Fue útil?

Solución

Actualizar

Esto ahora es posible con Roslyn (Visual Studio 2015). Puede build a code analizador para verificar un atributo personalizado


No creo que sea posible. ObsoleteAttribute es tratado especialmente por el compilador y se define en el estándar C #. ¿Por qué diablos no es aceptable el atributo obsoleto? Me parece que esta es precisamente la situación para la que fue diseñada, y logra precisamente lo que necesita.

También tenga en cuenta que Visual Studio también recoge las advertencias generadas por ObsoleteAttribute sobre la marcha, lo cual es muy útil.

No quiero ser inútil, solo me pregunto por qué no estás interesado en usarlo ...

Desafortunadamente, ObsoleteAttribute está sellado (probablemente en parte debido al tratamiento especial), por lo tanto, no puedes subclasificar tu propio atributo.

Desde el estándar de C #: -

  

El atributo Obsoleto se usa para marcar   tipos y miembros de tipos que deberían   ya no se utilizará.

     

Si un programa usa un tipo o miembro   Que se decora con el Obsoleto.   atributo, el compilador emite una   advertencia o un error. Específicamente, la   el compilador emite una advertencia si no hay error   Se proporciona el parámetro, o si el error   parámetro se proporciona y tiene la   valor falso. El compilador emite un   error si el parámetro de error es   especificado y tiene el valor verdadero.

¿Eso no resume tus necesidades? ... no lo harás mejor de lo que creo.

Otros consejos

No sé si esto funcionará, pero vale la pena intentarlo.

No puedes extender Obsoleto, porque es definitivo, pero quizás puedas crear tu propio atributo y marcar esa clase como obsoleta como esta:

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

Luego, cuando marque sus métodos con " MustRefactor " atributo, las advertencias de compilación podrían mostrar.

Dije " tal vez " y " podría " porque no he probado esto Por favor, dime si no funciona, así que eliminaré la respuesta.

Saludos!

ACTUALIZACIÓN: Probado. Genera una advertencia de tiempo de compilación, pero el mensaje de error parece gracioso, deberías verlo por ti mismo y elegir. Esto está muy cerca de lo que quería lograr.

ACTUALIZACIÓN2: Con este código genera estas advertencias (no muy bien, pero no creo que haya algo mejor).

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
{

}

En algunos compiladores puedes usar #warning para emitir una advertencia:

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

En los compiladores de Microsoft, normalmente puedes usar el mensaje pragma:

#pragma message ( "text" )

Usted mencionó .Net, pero no especificó si estaba programando con C / C ++ o C #. Si está programando en C #, debe saber que C # admite el formato #warning .

Actualmente estamos en medio de muchas refactorizaciones donde no pudimos arreglar todo de inmediato. Simplemente usamos el comando #warning preproc donde necesitamos regresar y mirar el código. Se muestra en la salida del compilador. No creo que puedas ponerlo en un método, pero podrías ponerlo solo dentro del método, y aún así es fácil de encontrar.

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

En VS 2008 (+ sp1) #warnings no se muestra correctamente en la Lista de errores después de Clean Soultion & amp; Solución de reconstrucción, no todos ellos. Algunas advertencias se muestran en la Lista de errores solo después de abrir un archivo de clase particular. Así que me vi obligado a usar un atributo personalizado:

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

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

    public MappingToDo()
    {}
}

Entonces, cuando marque algún código con él

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

Produce advertencias como esta:

  

Namespace.MappingToDo está obsoleto:   'Mapping ToDo'.

No puedo cambiar el texto de la advertencia, 'Algunos comentarios' no se muestra en la Lista de errores. Pero saltará al lugar adecuado en el archivo. Por lo tanto, si necesita modificar estos mensajes de advertencia, cree varios atributos.

Lo que estás tratando de hacer es un mal uso de los atributos. En su lugar, utilice la lista de tareas de Visual Studio. Puedes ingresar un comentario en tu código como este:

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

Luego abra Ver / Lista de Tareas desde el menú. La lista de tareas tiene dos categorías, tareas de usuario y comentarios. Cambie a Comentarios y verá todos sus // Todo: 's allí. Hacer doble clic en un TODO saltará al comentario en tu código.

Al

No creo que puedas. Por lo que sé, el soporte para ObsoleteAttribute está esencialmente codificado en el compilador C #; No puedes hacer nada similar directamente.

Lo que podría hacer es usar una tarea de MSBuild (o un evento posterior a la compilación) que ejecute una herramienta personalizada contra el ensamblado recién compilado. La herramienta personalizada se reflejaría en todos los tipos / métodos en el ensamblaje y consumiría su atributo personalizado, momento en el cual podría imprimir a los TextWriters predeterminados o de error de System.Console.

Buscando en la fuente de ObsoleteAttribute , no No parece que esté haciendo nada especial para generar una advertencia del compilador, así que tiendo a ir con @ tecnófilo y diga que está codificado en el compilador. ¿Hay alguna razón por la que no quiera usar ObsoleteAttribute para generar tus mensajes de advertencia?

Hay varios comentarios que sugieren insertar advertencias o pragma. ¡Las obras obsoletas de una manera muy diferente! Al marcar como obsoleta una función de una biblioteca L, el mensaje obsoleto se genera cuando un programa llama a la función, incluso si el programa que llama no está en la biblioteca L. La advertencia muestra el mensaje SOLAMENTE cuando L se compila.

Aquí está la implementación de Roslyn, para que pueda crear sus propios atributos que emitan advertencias o errores sobre la marcha.

He creado un atributo Tipo llamado IdeMessage que será el atributo que genera advertencias:

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

    public IDEMessageAttribute(string message);
}

Para hacer esto, primero debe instalar el SDK de Roslyn y comenzar un nuevo proyecto VSIX con analizador. He omitido algunas de las piezas menos relevantes, como los mensajes, puedes averiguar cómo hacerlo. En tu analizador haces esto

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

No hay CodeFixProvider para esto, puedes eliminarlo de la solución.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top