Peringatan Penyusun Kustom

115

Saat menggunakan ObsoleteAtribute di .Net, ini memberi Anda peringatan kompiler yang memberi tahu Anda bahwa objek / metode / properti sudah usang dan ada hal lain yang harus digunakan. Saat ini saya mengerjakan proyek yang membutuhkan banyak refactoring kode mantan karyawan. Saya ingin menulis atribut khusus yang dapat saya gunakan untuk menandai metode atau properti yang akan menghasilkan peringatan compiler yang memberikan pesan yang saya tulis. Sesuatu seperti ini

[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

Saya ingin ini menghasilkan peringatan kompiler yang mengatakan, "Kode ini sux dan harus dilihat". Saya tahu cara membuat atribut khusus, pertanyaannya adalah bagaimana cara membuatnya menghasilkan peringatan kompiler di studio visual.

Mikha
sumber
Apakah ini C #? Saya akan memberi tag ulang ini sebagai C # (bukan C) dengan anggapan itulah yang dimaksudkan untuk dipilih oleh poster asli.
Onorio Catenacci
13
Itu bukan VB atau C # yang valid ... jadi apa itu ...?!
ljs
5
Pertanyaan lama, tapi sekarang Anda bisa mendefinisikan peringatan compiler kustom menggunakan Roslyn.
RJ Cuthbertson
4
@jrummell Dalam pidato Roslyn, penganalisis kode: johnkoerner.com/csharp/creating-your-first-code-analyzer
RJ Cuthbertson
2
@RJCuthbertson Saya memindahkan komentar Anda ke jawaban yang diterima untuk memberikan perhatian yang layak.
jpaugh

Jawaban:

27

Memperbarui

Ini sekarang dimungkinkan dengan Roslyn (Visual Studio 2015). Anda dapat membangun sebuah analyzer kode untuk memeriksa atribut khusus


Saya tidak percaya itu mungkin. ObsoleteAttribute diperlakukan secara khusus oleh kompilator dan didefinisikan dalam standar C #. Mengapa ObsoleteAttribute tidak dapat diterima? Bagi saya, sepertinya inilah situasi yang dirancang untuk itu, dan mencapai tepat apa yang Anda butuhkan!

Perhatikan juga bahwa Visual Studio mengambil peringatan yang dihasilkan oleh ObsoleteAttribute dengan cepat juga, yang sangat berguna.

Bukan bermaksud tidak membantu, hanya bertanya-tanya mengapa Anda tidak tertarik untuk menggunakannya ...

Sayangnya ObsoleteAttribute disegel (mungkin sebagian karena perlakuan khusus) sehingga Anda tidak dapat membuat subkelas atribut Anda sendiri darinya.

Dari standar C #: -

Atribut Usang digunakan untuk menandai jenis dan anggota jenis yang tidak boleh lagi digunakan.

Jika program menggunakan tipe atau anggota yang didekorasi dengan atribut Usang, kompilator mengeluarkan peringatan atau kesalahan. Secara khusus, compiler mengeluarkan peringatan jika tidak ada parameter error yang diberikan, atau jika parameter error disediakan dan memiliki nilai false. Kompilator mengeluarkan kesalahan jika parameter kesalahan ditentukan dan memiliki nilai true.

Bukankah itu meringkas kebutuhan Anda? ... Anda tidak akan melakukan lebih baik dari yang saya kira.

ljs
sumber
14
Saya mencari hal yang sama. 'Karya' yang sudah usang tetapi kodenya tidak terlalu usang, melainkan juga tidak lengkap karena pemfaktoran ulang.
g.
11
Saya setuju dengan @g, dan mungkin penulis aslinya. Usang berarti usang, jangan digunakan. Saya ingin menandai sesuatu sebagai "hei ini mengkompilasi tetapi kita benar-benar perlu a) melengkapi fungsionalitas atau b) refactor". Ini akan lebih merupakan atribut waktu pengembangan. Juga tugas-tugas bekerja, misalnya // TODO :, tetapi saya tidak menggunakannya, karena saya rasa banyak orang tidak, tetapi tinjau peringatan kompiler secara teratur.
MikeJansen
8
Alasan lain untuk tidak menggunakan [Obsolete]tag adalah karena dapat menyebabkan masalah jika Anda perlu melakukan XmlSerialization dengan properti tersebut. Menambahkan [Obsolete]tag juga menambahkan [XmlIgnore]atribut di balik layar.
burnttoast11
6
Usang berbeda. Usang akan memberi Anda peringatan di setiap baris kode yang memanggil metode itu. Saya tidak berpikir itu yang diinginkan poster (setidaknya bukan itu yang saya inginkan ketika saya melakukan pencarian dan menemukan pertanyaan ini). Saya pikir pertanyaan yang dicari adalah peringatan untuk muncul pada definisi fungsi, bukan tempat yang digunakan.
Nick
Bukan jawaban terbaik. -1 untuk memikirkan ketidakmampuan Anda untuk mengemukakan alasan untuk tidak menggunakannya pantas dikritik. Sikap ini mematahkan semangat keaslian.
Mike Socha III
96

Ini patut dicoba.

Anda tidak dapat memperpanjang Usang, karena sudah final, tetapi mungkin Anda dapat membuat atribut Anda sendiri, dan menandai kelas tersebut sebagai usang seperti ini:

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

Kemudian saat Anda menandai metode Anda dengan atribut "MustRefactor", peringatan kompilasi akan ditampilkan. Ini menghasilkan peringatan waktu kompilasi, tetapi pesan kesalahan terlihat lucu, Anda harus melihatnya sendiri dan memilih. Ini sangat dekat dengan apa yang ingin Anda capai.

PEMBARUAN: Dengan kode ini Ini menghasilkan peringatan (tidak terlalu bagus, tapi saya rasa tidak ada yang lebih baik).

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
{

}
Pablo Fernandez
sumber
Bisakah Anda menempelkan apa yang dihasilkannya? Saya penasaran.
Mikha
1
Peringatan kompilasi dipicu bahkan jika Properti / Metode tidak dipanggil.
Rolf Kristensen
1
Saran bagus di sini. Saya ingin melakukan hal yang sama, dan akhirnya hanya membuang NotImplementedExceptions. Bukan solusi terbaik karena tidak muncul pada waktu kompilasi, hanya saat runtime jika kode tersebut dieksekusi. Saya akan mencobanya sendiri.
MonkeyWrench
1
Bukankah lebih bagus jika ObsolteAttribute dapat mendukung ekspresi seperti DebuggerDisplayAttribute, maka kita benar-benar dapat melakukan beberapa hal keren. visualstudio.uservoice.com/forums/121579-visual-studio/…
jpierson
Jika Anda menerapkan IDisposablepada kelas yang sudah usang tersebut, itu berarti Anda dapat menggabungkan kode pengujian cerdik Anda dalam satu usingblok. Seperti ini: using(new MustRefactor()){DodgyCode();}. Kemudian Anda dapat menemukan semua penggunaan setelah selesai. Saya menggunakan ini sekarang ke Sleeputas di dalam loop for yang saya butuhkan untuk memperlambat secara artifisial untuk tujuan debugging.
Iain Fraser
48

Di beberapa kompiler, Anda dapat menggunakan #warning untuk mengeluarkan peringatan:

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

Di kompiler Microsoft, Anda biasanya dapat menggunakan pesan pragma:

#pragma message ( "text" )

Anda menyebutkan .Net, tetapi tidak menentukan apakah Anda memprogram dengan C / C ++ atau C #. Jika Anda memprogram dalam C #, Anda harus tahu bahwa C # mendukung format #warning .

Douglas Mayle
sumber
1
#warning atau #pragma adalah arahan pra-prosesor dan dengan demikian akan berjalan terlepas dari keberadaan kode mantan kolega micah mana pun, dan tidak berinteraksi dengan atribut sama sekali. Cukup pasti Usang adalah satu-satunya cara untuk mencapai ini ...
ljs
39

Kami saat ini sedang melakukan banyak pemfaktoran ulang di mana kami tidak dapat langsung memperbaiki semuanya. Kami hanya menggunakan perintah preproc #warning di mana kami perlu kembali dan melihat kode. Ini muncul di keluaran kompiler. Saya tidak berpikir Anda dapat menerapkannya pada suatu metode, tetapi Anda dapat meletakkannya di dalam metode, dan itu masih mudah ditemukan.

public void DoEverything() {
   #warning "This code sucks"
}
Ted Elliott
sumber
7

Di VS 2008 (+ sp1) #warnings tidak ditampilkan dengan benar di Daftar Kesalahan setelah Solusi Bersihkan Soultion & Rebuild, tidak semuanya. Beberapa Peringatan hanya ditampilkan dalam Daftar Kesalahan setelah saya membuka file kelas tertentu. Jadi saya terpaksa menggunakan atribut khusus:

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

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

    public MappingToDo()
    {}
}

Jadi ketika saya menandai beberapa kode dengannya

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

Ini menghasilkan peringatan seperti ini:

Namespace.MappingToDo sudah usang: 'Mapping ToDo'.

Saya tidak bisa mengubah teks peringatan, 'Beberapa komentar' tidak ditampilkan Daftar Kesalahan. Tapi itu akan melompat ke tempat yang tepat dalam file. Jadi jika Anda perlu memvariasikan pesan peringatan tersebut, buatlah berbagai atribut.

Tomasz Modelski
sumber
6

Apa yang Anda coba lakukan adalah penyalahgunaan atribut. Sebaliknya gunakan Daftar Tugas Visual Studio. Anda dapat memasukkan komentar dalam kode Anda seperti ini:

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

Kemudian buka View / Task List dari menu. Daftar tugas memiliki dua kategori, tugas pengguna dan Komentar. Beralih ke Komentar dan Anda akan melihat semua // Todo: ada di sana. Mengklik dua kali pada TODO akan membuka komentar di kode Anda.

Al

pengguna4089256
sumber
1
saya menemukan ini solusi yang lebih disukai
Samuel
1
bagaimana jika Anda ingin menandai suatu fungsi sebagai "Tidak dipanggil dalam kode produksi" atau serupa. Jadi, Anda ingin mengaktifkannya jika fungsi atau kelas dipanggil atau dibuat instance-nya, tetapi tidak jika itu baru saja dikompilasi.
Jesse Pepper
2

Saya tidak berpikir Anda bisa. Sejauh yang saya tahu, dukungan untuk ObsoleteAttribute pada dasarnya di-hardcode ke dalam compiler C #; Anda tidak dapat melakukan hal serupa secara langsung.

Apa yang mungkin dapat Anda lakukan adalah menggunakan tugas MSBuild (atau acara pasca-pembuatan) yang menjalankan alat kustom terhadap rakitan yang baru saja dikompilasi. Alat kustom akan mencerminkan semua jenis / metode dalam perakitan dan menggunakan atribut kustom Anda, di mana ia dapat mencetak ke default System.Console atau kesalahan TextWriters.

teknofil
sumber
2

Melihat sumber untuk ObsoleteAttribute , sepertinya tidak melakukan sesuatu yang khusus untuk menghasilkan peringatan kompiler, jadi saya akan cenderung menggunakan @ technophile dan mengatakan bahwa itu dikodekan dengan keras ke dalam kompiler. Apakah ada alasan mengapa Anda tidak ingin hanya menggunakan ObsoleteAttribute untuk menghasilkan pesan peringatan Anda?

bdukes
sumber
Tidak ada alasan khusus selain kode yang belum tentu usang.
Mikha
1
Ini ditentukan dalam spesifikasi C # sebagai diperlakukan secara khusus oleh kompiler, lihat jawaban saya :-). Mikha - 'Atribut Usang digunakan untuk menandai jenis dan anggota jenis yang seharusnya tidak lagi digunakan.' dari spesifikasi. Bukankah itu berlaku? ...
ljs
Hanya jika seseorang bertanya-tanya, tidak ada kode C # dalam kode sumber untuk melakukan ini juga. referenceource.microsoft.com/#mscorlib/system/…
Paweł Mach
1

Ada beberapa komentar yang menyarankan untuk memasukkan peringatan atau pragma. Karya usang dengan cara yang sangat berbeda! Menandai fungsi usang dari pustaka L, pesan usang muncul saat program memanggil fungsi tersebut meskipun program pemanggil tidak ada di pustaka L. Peringatan memunculkan pesan HANYA saat L dikompilasi.

bubi
sumber
1

Berikut adalah Implementasi Roslyn, sehingga Anda dapat membuat atribut Anda sendiri yang memberikan peringatan atau kesalahan dengan cepat.

Saya telah membuat atribut Type Called IdeMessageyang akan menjadi atribut yang menghasilkan peringatan:

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

    public IDEMessageAttribute(string message);
}

Untuk melakukan ini, Anda perlu menginstal Roslyn SDK terlebih dahulu dan memulai proyek VSIX baru dengan analyzer. Saya telah menghilangkan beberapa bagian yang kurang relevan seperti pesan, Anda dapat mengetahui cara melakukannya. Dalam penganalisis Anda, Anda melakukan ini

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

Tidak ada CodeFixProvider untuk ini, Anda dapat menghapusnya dari solusi.

johnny 5
sumber