Aracılığıyla paylaş


ImmutableArrays için Roslyn çözümleyicileri ve kod kullanan kitaplık

.NET Derleyici Platformu ("Roslyn"), kod kullanan kitaplıklar oluşturmanıza yardımcı olur. Kod kullanan kitaplık, kitaplığı en iyi şekilde kullanmanıza veya hataları önlemenize yardımcı olmak için kullanabileceğiniz işlevler ve araçlar (Roslyn çözümleyicileri) sağlar. Bu konu başlığında, System.Collections.Immutable NuGet paketini kullanırken sık karşılaşılan hataları yakalamak için gerçek bir Roslyn çözümleyicisi oluşturma adımları gösterilmektedir. Örnekte ayrıca çözümleyici tarafından bulunan bir kod sorunu için kod düzeltmesinin nasıl sağlandığı da gösterilmektedir. Kullanıcılar Visual Studio ampul kullanıcı arabiriminde kod düzeltmelerini görür ve kod için otomatik olarak bir düzeltme uygulayabilir.

Kullanmaya başlayın

Bu örneği oluşturmak için aşağıdakilere ihtiyacınız vardır:

  • Visual Studio 2015 (Express Edition değil) veya sonraki bir sürüm. Ücretsiz Visual Studio Community Edition'ı kullanabilirsiniz
  • Visual Studio SDK'sı. Ayrıca, Visual Studio'yu yüklerken, SDK'yı aynı anda yüklemek için Ortak Araçlar'ın altındaki Visual Studio Genişletilebilirlik Araçları'nı da kontrol edebilirsiniz. Visual Studio'yu zaten yüklediyseniz bu SDK'yı yüklemek için Dosya Yeni>Proje ana menüsüne> gidin, sol gezinti bölmesinde C# seçeneğini belirleyin ve Genişletilebilirlik'i seçin. "Visual Studio Genişletilebilirlik Araçlarını Yükleme" içerik haritası proje şablonunu seçtiğinizde, SDK'yı indirip yüklemeniz istenir.
  • .NET Derleyici Platformu ("Roslyn") SDK'sı. Ayrıca bu SDK'yı yüklemek için Dosya Yeni>Proje ana menüsüne> gidip sol gezinti bölmesinde C# seçeneğini belirleyip Genişletilebilirlik'i seçebilirsiniz. ".NET Derleyici Platformu SDK'sını İndir" içerik haritası proje şablonunu seçtiğinizde, SDK'yı indirip yüklemeniz istenir. Bu SDK Roslyn Sözdizimi Görselleştiricisini içerir. Bu kullanışlı araç, çözümleyicinizde hangi kod modeli türlerini aramanız gerektiğini öğrenmenize yardımcı olur. Çözümleyici altyapısı belirli kod modeli türleri için kodunuz için çağrılar, böylece kodunuz yalnızca gerektiğinde yürütülür ve yalnızca ilgili kodu çözümlemeye odaklanabilir.

Sorun nedir?

ImmutableArray (örneğin, System.Collections.Immutable.ImmutableArray<T>) desteğine sahip bir kitaplık sağladığınızı düşünün. C# geliştiricileri .NET dizileri konusunda çok fazla deneyime sahiptir. Ancak, ImmutableArrays ve uygulamada kullanılan iyileştirme tekniklerinin doğası gereği, C# geliştirici sezgileri kitaplığınızın kullanıcılarının aşağıda açıklandığı gibi bozuk kod yazmasına neden olur. Ayrıca, kullanıcılar çalışma zamanına kadar hatalarını görmez. Bu, .NET ile Visual Studio'da alışkın oldukları kalite deneyimi değildir.

Kullanıcılar aşağıdaki gibi kod yazma konusunda bilgi sahibidir:

var a1 = new int[0];
Console.WriteLine("a1.Length = { 0}", a1.Length);
var a2 = new int[] { 1, 2, 3, 4, 5 };
Console.WriteLine("a2.Length = { 0}", a2.Length);

Sonraki kod satırlarıyla doldurmak için boş diziler oluşturmak ve koleksiyon başlatıcı söz dizimini kullanmak C# geliştiricilerine tanıdık geliyor. Ancak, bir ImmutableArray için aynı kodu yazmak çalışma zamanında kilitlenir:

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = { 0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = { 0}", b2.Length);

İlk hata, ImmutableArray uygulamasının temel alınan veri depolama alanını sarmak için bir yapı kullanması nedeniyledir. İfadelerin sıfır veya null üyeleri olan default(T) yapıları döndürebilmesi için yapıların parametresiz oluşturucuları olmalıdır. Kod eriştiğinde b1.Length, ImmutableArray yapısında temel alınan depolama dizisi olmadığından bir çalışma zamanı null başvuru kaldırma hatası olur. Boş bir ImmutableArray oluşturmanın doğru yoludur ImmutableArray<int>.Empty.

Koleksiyon başlatıcılarıyla ilgili hata, yöntemi her çağırdığınızda yeni örnekler döndürdüğünden ImmutableArray.Add oluşur. ImmutableArrays hiçbir zaman değişmediğinden, yeni bir öğe eklediğinizde yeni bir ImmutableArray nesnesi alırsınız (daha önce var olan bir ImmutableArray ile performans nedenleriyle depolamayı paylaşabilir). b2 Beş kez çağırmadan Add() önce ilk ImmutableArray'ye işaret ettiğinden, b2 varsayılan bir ImmutableArray değeridir. Üzerinde Uzunluk çağrısı null başvuru hatasıyla da kilitleniyor. El ile Add çağrısı yapmadan bir ImmutableArray başlatmanın doğru yolu kullanmaktır ImmutableArray.CreateRange(new int[] {1, 2, 3, 4, 5}).

Çözümleyicinizi tetikleyen ilgili söz dizimi düğüm türlerini bulma

Çözümleyiciyi oluşturmaya başlamak için önce ne tür bir SözdizimiNode'a bakmanız gerektiğini öğrenin. Diğer Windows>Roslyn Sözdizimi Görselleştiricisini Görüntüle>menüsünden Söz Dizimi Görselleştiricisi'ni başlatın.

Düzenleyicinin şapka işaretini bildiren b1satıra yerleştirin. Söz Dizimi Görselleştiricisi söz dizimi ağacının bir LocalDeclarationStatement düğümünde olduğunuzu gösterir. Bu düğüm, sırayla bir VariableDeclaratoröğesine ve son olarak da bir öğesine sahip olan bir ObjectCreationExpressionEqualsValueClauseöğesine sahiptirVariableDeclaration. Düğümlerin Söz Dizimi Görselleştiricisi ağacına tıkladığınızda, düzenleyici penceresindeki söz dizimi size bu düğümün temsil ettiği kodu gösterecek şekilde vurgulanır. SyntaxNode alt türlerinin adları C# dil bilgisinde kullanılan adlarla eşleşer.

Çözümleyici projesini oluşturma

Ana menüden Dosya Yeni Proje'yi> seçin.> Yeni Proje iletişim kutusunda, sol gezinti çubuğundaki C# projeleri'nin altında Genişletilebilirlik'i seçin ve sağ bölmede Kod Düzeltmesi ile Çözümleyici proje şablonunu seçin. Bir ad girin ve iletişim kutusunu onaylayın.

Şablon bir DiagnosticAnalyzer.cs dosyası açar. Düzenleyici arabelleği sekmesini seçin. Bu dosyada ( Roslyn API türünde) türetilen bir çözümleyici sınıfı (projeye eklediğiniz addan DiagnosticAnalyzer oluşturulmuş) vardır. Yeni sınıfınızda çözümleyicinizin C# diliyle ilgili olduğunu belirten bir DiagnosticAnalyzerAttribute bildirim var, böylece derleyici çözümleyicinizi bulur ve yükler.

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ImmutableArrayAnalyzerAnalyzer : DiagnosticAnalyzer
{}

Visual Basic kullanarak C# kodunu hedefleyen bir çözümleyici uygulayabilirsiniz ve tam tersi de geçerlidir. Çözümleyicinizin bir dili mi yoksa her ikisini birden mi hedeflediğini seçmek DiagnosticAnalyzerAttribute içinde daha önemlidir. Dilin ayrıntılı modellemesini gerektiren daha gelişmiş çözümleyiciler yalnızca tek bir dili hedefleyebilir. Örneğin çözümleyiciniz yalnızca tür adlarını veya ortak üye adlarını denetlerse, Roslyn'in sunduğu ortak dil modelini Visual Basic ve C# genelinde kullanmak mümkün olabilir. Örneğin, FxCop bir sınıfın uygulaması konusunda uyarır ISerializable, ancak sınıfın özniteliği dilden SerializableAttribute bağımsızdır ve hem Visual Basic hem de C# kodu için çalışır.

Çözümleyiciyi başlatma

Yöntemini görmek Initialize için sınıfta biraz DiagnosticAnalyzer aşağı kaydırın. Derleyici bir çözümleyiciyi etkinleştirirken bu yöntemi çağırır. yöntemi, çözümleyicinizin bağlam bilgilerini almasını ve çözümlemek istediğiniz kod türleri için olaylar için geri çağırmaları kaydetmesini sağlayan bir AnalysisContext nesnesi alır.

public override void Initialize(AnalysisContext context) {}

IntelliSense tamamlama listesini görmek için bu yöntemde yeni bir satır açın ve "context" yazın. Tamamlanma listesinde çeşitli olay türlerini işlemek için birçok Register... yöntem olduğunu görebilirsiniz. Örneğin, ilki olan , RegisterCodeBlockActiongenellikle küme ayraçları arasındaki kod olan bir blok için kodunuz için geri çağrır. Bir blok için kaydolmak, bir alanın başlatıcısı, bir özniteliğe verilen değer veya isteğe bağlı bir parametrenin değeri için kodunuz için de geri çağrır.

Başka bir örnek olarak, RegisterCompilationStartActionderlemenin başlangıcında kodunuz için geri çağrılar. Bu, birçok konum üzerinde durum toplamanız gerektiğinde kullanışlıdır. Kullanılan tüm simgeleri toplamak için bir veri yapısı oluşturabilirsiniz ve çözümleyiciniz her söz dizimi veya simge için geri çağrıldığında veri yapınızdaki her konumla ilgili bilgileri kaydedebilirsiniz. Derlemenin sona ermesi nedeniyle geri çağrıldığında, kaydettiğiniz tüm konumları analiz edebilir, örneğin, kodun her using deyimden hangi simgeleri kullandığını raporlayabilirsiniz.

Söz Dizimi Görselleştiricisi'ni kullanarak, derleyici bir ObjectCreationExpression işlediğinde çağrılmak istediğinizi öğrendiniz. Geri çağırmayı ayarlamak için bu kodu kullanırsınız:

context.RegisterSyntaxNodeAction(c => AnalyzeObjectCreation(c),
                                 SyntaxKind.ObjectCreationExpression);

Bir söz dizimi düğümüne kaydolun ve yalnızca nesne oluşturma söz dizimi düğümleri için filtre uygulayın. Kural gereği çözümleyici yazarları eylemleri kaydederken lambda kullanır ve bu da çözümleyicilerin durum bilgisi olmayan olmasına yardımcı olur. Yöntemini oluşturmak AnalyzeObjectCreation için Visual Studio Kullanımdan Oluştur özelliğini kullanabilirsiniz. Bu, sizin için de doğru bağlam parametresi türünü oluşturur.

Çözümleyicinizin kullanıcıları için özellikleri ayarlama

Çözümleyicinizin Visual Studio kullanıcı arabiriminde uygun şekilde görünmesi için çözümleyicinizi tanımlamak için aşağıdaki kod satırını arayın ve değiştirin:

internal const string Category = "Naming";

"Naming" değerini "API Guidance" olarak değiştirin.

Ardından, Çözüm Gezgini kullanarak projenizde Resources.resx dosyasını bulun ve açın. Çözümleyiciniz, başlığınız vb. için bir açıklama ekleyebilirsiniz. Bunların tümünün değerini şimdilik olarak "Don't use ImmutableArray<T> constructor" değiştirebilirsiniz. Dize biçimlendirme bağımsız değişkenlerini dizenize ({0}, vb {1}.) yerleştirebilirsiniz ve daha sonra çağırdığınızda Diagnostic.Create()geçirilecek bir params dizi bağımsız değişken sağlayabilirsiniz.

Nesne oluşturma ifadesini analiz etme

yöntemi, AnalyzeObjectCreation kod çözümleyici çerçevesi tarafından sağlanan farklı bir bağlam türünü alır. Initialize yöntemiAnalysisContext, çözümleyicinizi ayarlamak için eylem geri çağırmalarını kaydetmenize olanak tanır. SyntaxNodeAnalysisContextörneğin, içinde geçirebileceğiniz bir CancellationToken vardır. Kullanıcı düzenleyicide yazmaya başlarsa Roslyn, çalışmayı kaydetmek ve performansı geliştirmek için çalışan çözümleyicileri iptal eder. Başka bir örnek olarak, bu bağlamın nesne oluşturma söz dizimi düğümünü döndüren bir Node özelliği vardır.

Söz dizimi düğümü eylemini filtrelediğiniz tür olduğunu varsayabileceğiniz düğümü alın:

var objectCreation = (ObjectCreationExpressionSyntax)context.Node;

Visual Studio'yu çözümleyicinizle ilk kez başlatma

Çözümleyicinizi oluşturup yürüterek Visual Studio'yu başlatın (F5 tuşuna basın). Çözüm Gezgini başlangıç projesi VSIX projesi olduğundan, kodunuzu çalıştırmak kodunuzu ve bir VSIX'i oluşturur ve ardından bu VSIX yüklü olarak Visual Studio'yu başlatır. Visual Studio'yu bu şekilde başlattığınızda, Visual Studio'yu temel kullanımınız çözümleyiciler oluştururken test örneklerinizden etkilenmemesi için ayrı bir kayıt defteri kovanı ile başlatılır. Bu şekilde ilk kez başlattığınızda Visual Studio, visual studio'yu yükledikten sonra ilk kez başlattığınızdakine benzer birkaç başlatma işlemi yapar.

Bir konsol projesi oluşturun ve ardından dizi kodunu konsol uygulamalarınıza Main yöntemine girin:

var b1 = new ImmutableArray<int>();
Console.WriteLine("b1.Length = {0}", b1.Length);
var b2 = new ImmutableArray<int> { 1, 2, 3, 4, 5 };
Console.WriteLine("b2.Length = {0}", b2.Length);

sabit NuGet paketini almanız ve kodunuza bir using deyim eklemeniz gerektiğinden ile kod ImmutableArray satırlarında dalgalı çizgiler vardır. Çözüm Gezgini proje düğümünde sağ işaretçi düğmesine basın ve NuGet Paketlerini Yönet'i seçin. NuGet yöneticisinde, arama kutusuna "Sabit" yazın ve sol bölmedeki System.Collections.Immutable öğesini seçin (Microsoft.Bcl.Immutable'ı seçmeyin) ve sağ bölmedeki Yükle düğmesine basın . Paketin yüklenmesi, proje başvurularınıza bir başvuru ekler.

altında ImmutableArrayhala kırmızı dalgalı çizgiler görürsünüz, bu nedenle şapka işaretini bu tanımlayıcıya yerleştirin ve önerilen düzeltme menüsünü açmak ve uygun using deyimi eklemeyi seçmek için Ctrl+ tuşuna (nokta) basın.

Devam etmek üzere temiz bir duruma getirmek için Şimdilik Tümünü Kaydet ve Visual Studio'nun ikinci örneğini kapat .

Düzenlemeyi kullanarak çözümleyiciyi bitirin ve devam edin

Visual Studio'nun ilk örneğinde, ilk satırdaki şapka işaretiyle F9 tuşuna basarak yönteminizin AnalyzeObjectCreation başında bir kesme noktası ayarlayın.

F5 ile çözümleyicinizi yeniden başlatın ve Visual Studio'nun ikinci örneğinde, en son oluşturduğunuz konsol uygulamanızı yeniden açın.

Roslyn derleyicisi bir Nesne oluşturma ifadesi gördüğünden ve çözümleyicinize çağrıldığından, kesme noktasında Visual Studio'nun ilk örneğine dönersiniz.

Nesne oluşturma düğümünü alın. F10 tuşuna basarak değişkeni ayarlayan objectCreation çizginin üzerine geçin ve Anında Penceresinde ifadesini "objectCreation.ToString()"değerlendirin. Değişkenin işaret olduğu söz dizimi düğümünü tam aradığınız kod "new ImmutableArray<int>()"olduğunu görürsünüz.

ImmutableArray<T> Type nesnesini alın. Oluşturulan türün ImmutableArray olup olmadığını denetlemeniz gerekir. İlk olarak, bu türü temsil eden nesneyi alırsınız. Tam olarak doğru türe sahip olduğunuzdan ve dizesini ToString()karşılaştırmadığınızdan emin olmak için anlamsal modeli kullanarak türleri denetlersiniz. İşlevin sonuna aşağıdaki kod satırını girin:

var immutableArrayOfTType =
    context.SemanticModel
           .Compilation
           .GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1");

Meta verilerdeki genel türleri, backticks (') ve genel parametre sayısıyla belirtirsiniz. Bu yüzden "... Meta veri adında ImmutableArray<T>" yazın.

Anlamsal modelde simgeler, veri akışı, değişken yaşam süresi vb. hakkında sorular sormanıza olanak sağlayan birçok yararlı özellik vardır. Roslyn, söz dizimi düğümlerini çeşitli mühendislik nedenleriyle (performans, hatalı kod modelleme vb.) anlamsal modelden ayırır. Derleme modelinin doğru karşılaştırma için başvurularda yer alan bilgileri aramasını istiyorsunuz.

Düzenleyici penceresinin sol tarafındaki sarı yürütme işaretçisini sürükleyebilirsiniz. Değişkeni ayarlayan objectCreation satıra sürükleyin ve F10 kullanarak yeni kod satırınızın üzerine gidin. Fare işaretçisini değişkeninin immutableArrayOfTypeüzerine getirirseniz, anlam modelinde tam türü bulduğumuzu görürsünüz.

Nesne oluşturma ifadesinin türünü alın. "Tür", bu makalede birkaç şekilde kullanılır, ancak "yeni Foo" ifadeniz varsa bir Foo modeli almanız gerektiği anlamına gelir. ImmutableArray<T> türü olup olmadığını görmek için nesne oluşturma ifadesinin türünü almanız gerekir. Nesne oluşturma ifadesindeki tür simgesi (ImmutableArray) için sembol bilgilerini almak için semantik modeli yeniden kullanın. İşlevin sonuna aşağıdaki kod satırını girin:

var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;

Çözümleyicinizin düzenleyici arabelleklerinde eksik veya yanlış kodu işlemesi gerektiğinden (örneğin, eksik using bir deyim var) olup olmadığını nulldenetlemeniz symbolInfo gerekir. Çözümlemeyi tamamlamak için sembol bilgileri nesnesinden adlandırılmış bir tür (INamedTypeSymbol) almanız gerekir.

Türleri karşılaştırın. Aradığımız açık bir genel T türü olduğundan ve koddaki tür somut bir genel tür olduğundan, türün hangi türden (açık bir genel tür) oluştuğuna ilişkin simge bilgilerini sorgular ve bu sonucu ile immutableArrayOfTTypekarşılaştırırsınız. Yönteminin sonuna aşağıdakileri girin:

if (symbolInfo != null &&
    symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
{}

Tanılamayı bildirin. Tanılamayı raporlamak oldukça kolaydır. Sizin için oluşturulan Kuralı, Initialize yönteminden önce tanımlanan proje şablonunda kullanırsınız. Koddaki bu durum bir hata olduğundan, Kuralı DiagnosticSeverity.Warning başlatan satırı değiştirerek (yeşil dalgalı çizgi) değerini (kırmızı dalgalı) değiştirebilirsiniz DiagnosticSeverity.Error . Kuralın geri kalanı, kılavuzun başında düzenlediğiniz kaynaklardan başlatılır. Ayrıca, nesne oluşturma ifadesinin tür belirtiminin konumu olan dalgalı çizginin konumunu da raporlamanız gerekir. Şu kodu bloğuna if girin:

context.ReportDiagnostic(Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));

İşleviniz şu şekilde görünmelidir (farklı biçimlendirilmiş olabilir):

private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
{
    var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
    var immutableArrayOfTType =
        context.SemanticModel
               .Compilation
               .GetTypeByMetadataName(
                   "System.Collections.Immutable.ImmutableArray`1");
    var symbolInfo = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as
        INamedTypeSymbol;
    if (symbolInfo != null &&
        symbolInfo.ConstructedFrom.Equals(immutableArrayOfTType))
    {
        context.ReportDiagnostic(
            Diagnostic.Create(Rule, objectCreation.Type.GetLocation()));
    }
}

Çözümleyicinizin çalıştığını görebilmek için kesme noktasını kaldırın (ve Visual Studio'nun ilk örneğine geri dönmeyi durdurun). Yürütme işaretçisini yönteminizin başına sürükleyin ve yürütmeye devam etmek için F5 tuşuna basın. Visual Studio'nun ikinci örneğine geri döndüğünüzde, derleyici kodu yeniden incelemeye başlar ve çözümleyicinize çağrır. altında ImmutableType<int>bir dalgalı çizgi görebilirsiniz.

Kod Sorunu için "Kod Düzeltmesi" Ekleme

Başlamadan önce, Visual Studio'nun ikinci örneğini kapatın ve Visual Studio'nun ilk örneğinde (çözümleyiciyi geliştirdiğiniz) hata ayıklamayı durdurun.

Yeni bir sınıf ekleyin. Çözüm Gezgini proje düğümünüzün kısayol menüsünü (sağ işaretçi düğmesi) kullanın ve yeni bir öğe eklemeyi seçin. adlı BuildCodeFixProviderbir sınıf ekleyin. Bu sınıfın öğesinden CodeFixProvidertüretilmesi gerekir ve doğru using deyimi ekleyen kod düzeltmesini çağırmak için Ctrl+. (nokta) kullanmanız gerekir. Bu sınıfın ayrıca özniteliğiyle ExportCodeFixProvider ek açıklama eklenmesi gerekir ve numaralandırmayı çözümlemek LanguageNames için bir using deyim eklemeniz gerekir. Aşağıdaki kodu içeren bir sınıf dosyanız olmalıdır:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;

namespace ImmutableArrayAnalyzer
{
    [ExportCodeFixProvider(LanguageNames.CSharp)]
    class BuildCodeFixProvider : CodeFixProvider
    {}

Türetilmiş üyeleri saplama. Şimdi düzenleyicinin şapka işaretini tanımlayıcıya CodeFixProvider yerleştirin ve bu soyut temel sınıf için uygulamayı saptamak için Ctrl+. (nokta) tuşlarına basın. Bu, sizin için bir özellik ve yöntem oluşturur.

özelliğini uygulayın. Özelliğin FixableDiagnosticIdsget gövdesini aşağıdaki kodla doldurun:

return ImmutableArray.Create(ImmutableArrayAnalyzerAnalyzer.DiagnosticId);

Roslyn, yalnızca dize olan bu tanımlayıcıları eşleştirerek tanılamaları ve düzeltmeleri bir araya getirir. Proje şablonu sizin için bir tanılama kimliği oluşturdu ve bunu değiştirebilirsiniz. özelliğindeki kod yalnızca çözümleyici sınıfından kimliği döndürür.

RegisterCodeFixAsync yöntemi bir bağlam alır. Kod düzeltmesi birden çok tanılamaya uygulanabileceğinden veya bir kod satırında birden fazla sorun olabileceğinden bağlam önemlidir. Yöntemin gövdesine "context" yazarsanız, IntelliSense tamamlama listesi size bazı yararlı üyeler gösterir. Bir şeyin düzeltmeyi iptal etmek isteyip istemediğini kontrol etmek için de görebileceğiniz bir CancellationToken üyesi vardır. Çok sayıda yararlı üyesi olan ve proje ve çözüm modeli nesnelerine ulaşabilmenizi sağlayan bir Belge üyesi vardır. Tanılamayı bildirdiğinizde belirtilen kod konumunun başlangıç ve bitişi olan bir Span üyesi vardır.

Yöntemin zaman uyumsuz olmasını sağlayın. Yapmanız gereken ilk şey, oluşturulan yöntem bildirimini bir async yöntem olacak şekilde düzeltmektir. Soyut bir sınıfın uygulanmasını saptamak için kod düzeltmesi, yöntemi bir Taskdöndürse bile anahtar sözcüğünü içermezasync.

Söz dizimi ağacının kökünü alın. Kodu değiştirmek için, kod düzeltmenizin yaptığı değişikliklerle yeni bir söz dizimi ağacı oluşturmanız gerekir. çağrısı GetSyntaxRootAsyncyapmak için bağlamdan öğesine ihtiyacınız vardırDocument. Bu zaman uyumsuz bir yöntemdir çünkü söz dizimi ağacını almak için büyük olasılıkla dosyayı diskten alma, ayrıştırma ve roslyn kod modelini oluşturma gibi bilinmeyen bir çalışma vardır. Visual Studio kullanıcı arabiriminin bu süre boyunca yanıt vermesi gerekir ve bu da etkinleştirmeleri kullanarak async değiştirilebilir. yöntemindeki kod satırını aşağıdakilerle değiştirin:

var root = await context.Document
                        .GetSyntaxRootAsync(context.CancellationToken);

Sorunla ilgili düğümü bulun. Bağlamın aralığını geçirirsiniz, ancak bulduğunuz düğüm değiştirmeniz gereken kod olmayabilir. Bildirilen tanılama yalnızca tür tanımlayıcısı için yayılma alanı sağladı (dalgalı çizginin ait olduğu yer), ancak baştaki anahtar sözcük ve sonunda parantezler de dahil olmak üzere new nesne oluşturma ifadesinin tamamını değiştirmeniz gerekiyor. Yönteminize aşağıdaki kodu ekleyin (ve için bir deyim eklemek için ObjectCreationExpressionSyntaxCtrl+ tuşunu kullanın):using

var objectCreation = root.FindNode(context.Span)
                         .FirstAncestorOrSelf<ObjectCreationExpressionSyntax>();

Ampul kullanıcı arabirimi için kod düzeltmenizi kaydedin. Kod düzeltmenizi kaydettiğinizde Roslyn, Visual Studio ampul kullanıcı arabirimine otomatik olarak takılır. Çözümleyiciniz hatalı ImmutableArray<T> bir oluşturucu kullanımına engel olduğunda son kullanıcılar Ctrl+ tuşunu (nokta) kullanabileceklerini görür. Kod düzeltme sağlayıcınız yalnızca bir sorun olduğunda yürütülür çünkü aradığınız nesne oluşturma ifadesine sahip olduğunuzu varsayabilirsiniz. Bağlam parametresinden, aşağıdaki kodu yöntemin sonuna RegisterCodeFixAsync ekleyerek yeni kod düzeltmesini kaydedebilirsiniz:

context.RegisterCodeFix(
            CodeAction.Create("Use ImmutableArray<T>.Empty",
                              c => ChangeToImmutableArrayEmpty(objectCreation,
                                                               context.Document,
                                                               c)),
            context.Diagnostics[0]);

Düzenleyicinin şapka işaretini tanımlayıcısına CodeActionyerleştirmeniz, ardından bu tür için uygun using deyimi eklemek için Ctrl+. (nokta) tuşlarını kullanmanız gerekir.

Ardından düzenleyicinin şapka işaretini tanımlayıcıya ChangeToImmutableArrayEmpty yerleştirin ve Ctrl+ tuşunu yeniden kullanarak sizin için bu yöntem saplamasını oluşturun.

Eklediğiniz bu son kod parçacığı, bulunan sorunun türü için bir CodeAction ve tanılama kimliği geçirerek kod düzeltmesini kaydeder. Bu örnekte, bu kodun düzeltmeler sağladığı tek bir tanılama kimliği vardır, bu nedenle tanılama kimlikleri dizisinin ilk öğesini geçirebilirsiniz. oluşturduğunuzda CodeAction, ampul kullanıcı arabiriminin kod düzeltmesinin açıklaması olarak kullanması gereken metni geçirirsiniz. Ayrıca CancellationToken alan ve yeni bir Belge döndüren bir işlev de geçirirsiniz. Yeni Belgede, öğesini çağıran ImmutableArray.Emptydüzeltme eki uygulanmış kodunuzu içeren yeni bir söz dizimi ağacı vardır. Bu kod parçacığı, objectCreation düğümü ve bağlamın Belgesi üzerinden kapanabilmesi için bir lambda kullanır.

Yeni söz dizimi ağacını oluşturma. Saptamasını ChangeToImmutableArrayEmpty daha önce oluşturduğunuz yöntemine kod satırını girin: ImmutableArray<int>.Empty;. Söz Dizimi Görselleştiricisi araç penceresini yeniden görüntülerseniz, bu söz diziminin simpleMemberAccessExpression düğümü olduğunu görebilirsiniz. Bu yöntemin yeni bir Belgede oluşturması ve döndürmesi gereken budur.

kod oluşturucuları yöntemin zaman uyumsuz olması gerektiğini varsayamadığından ilk olarak öğesinin eklenmesi ChangeToImmutableArrayEmptyasyncTask<Document> gerekir.

Yönteminizin aşağıdakine benzer şekilde görünmesi için gövdeyi aşağıdaki kodla doldurun:

private async Task<Document> ChangeToImmutableArrayEmpty(
    ObjectCreationExpressionSyntax objectCreation, Document document,
    CancellationToken c)
{
    var generator = SyntaxGenerator.GetGenerator(document);
    var memberAccess =
        generator.MemberAccessExpression(objectCreation.Type, "Empty");
    var oldRoot = await document.GetSyntaxRootAsync(c);
    var newRoot = oldRoot.ReplaceNode(objectCreation, memberAccess);
    return document.WithSyntaxRoot(newRoot);
}

Düzenleyicinin şapka işaretini SyntaxGenerator tanımlayıcıya yerleştirmeniz ve bu tür için uygun using deyimi eklemek için Ctrl+. (nokta) tuşlarını kullanmanız gerekir.

Bu kod, SyntaxGeneratoryeni kod oluşturmak için kullanışlı bir tür olan kullanır. Kod sorunu olan belge için bir oluşturucu aldıktan sonra çağrısı ChangeToImmutableArrayEmptyMemberAccessExpressionyaparak erişmek istediğimiz üyeye sahip olan türü geçirir ve üyenin adını dize olarak geçirir.

Daha sonra yöntemi belgenin kökünü getirir ve bu genel durumda rastgele bir çalışma içerebileceğinden kod bu çağrıyı bekler ve iptal belirtecini geçirir. Roslyn kod modelleri, .NET dizesiyle çalışma gibi sabittir; dizesini güncelleştirdiğinizde, karşılığında yeni bir dize nesnesi alırsınız. çağrısı ReplaceNodeyaptığınızda yeni bir kök düğüm alırsınız. Söz dizimi ağacının çoğu paylaşılır (sabit olduğundan), ancak objectCreation düğüm, söz dizimi ağacı köküne kadar olan tüm üst düğümlerin yanı sıra düğümle memberAccess değiştirilir.

Kod düzeltmenizi deneyin

Artık çözümleyicinizi Visual Studio'nun ikinci bir örneğinde yürütmek için F5 tuşuna basabilirsiniz. Daha önce kullandığınız konsol projesini açın. Şimdi ampulün yeni nesne oluşturma ifadenizin için ImmutableArray<int>olduğu yerde göründüğünü görmeniz gerekir. Ctrl+. (nokta) tuşlarına basarsanız kod düzeltmenizi görürsünüz ve ampul kullanıcı arabiriminde otomatik olarak oluşturulan bir kod farkı önizlemesi görürsünüz. Roslyn bunu sizin için oluşturur.

Pro İpucu: Visual Studio'nun ikinci örneğini başlatırsanız ve kod düzeltmenizle ampulü görmüyorsanız Visual Studio bileşen önbelleğini temizlemeniz gerekebilir. Önbelleğin temizlenmesi, Visual Studio'yu bileşenleri yeniden incelemeye zorlar, böylece Visual Studio en son bileşeninizi almalıdır. İlk olarak, Visual Studio'nun ikinci örneğini kapatın. Ardından, Windows Gezgini'nde %LOCALAPPDATA%\Microsoft\VisualStudio\16.0Roslyn\ adresine gidin. ("16.0", Visual Studio ile sürümden sürüme değişir.) ComponentModelCache alt dizinini silin.

Video konuşma ve kod projelerini bitirme

Tamamlanmış kodun tamamını burada görebilirsiniz. DoNotUseImmutableArrayCollectionInitializer ve DoNotUseImmutableArrayCtor alt klasörlerinin her birinde sorunları bulmak için bir C# dosyası ve Visual Studio ampul kullanıcı arabiriminde görünen kod düzeltmelerini uygulayan bir C# dosyası bulunur. ImmutableArray<T> türündeki nesnesinin tekrar tekrar getirilmemesi için tamamlanmış kodun biraz daha soyutlaması olduğunu unutmayın. Tür nesnesini alt eylemler (nesne oluşturmayı analiz etme ve koleksiyon başlatmalarını analiz etme) her çalıştırıldığında kullanılabilen bir bağlamda kaydetmek için iç içe kaydedilmiş eylemleri kullanır.