Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu öğreticide Söz Dizimi API'sini keşfedeceksiniz. Söz Dizimi API'si, C# veya Visual Basic programını açıklayan veri yapılarına erişim sağlar. Bu veri yapıları, herhangi bir boyuttaki herhangi bir programı tam olarak temsil edecek kadar ayrıntıya sahiptir. Bu yapılar, doğru derlenen ve çalışan tam programları açıklayabilir. Ayrıca, siz yazarken tamamlanmamış programları düzenleyicide açıklayabilirler.
Bu zengin ifadeyi etkinleştirmek için Söz Dizimi API'sini oluşturan veri yapıları ve API'ler mutlaka karmaşıktır. Tipik "Merhaba Dünya" programı için veri yapısının nasıl göründüğüyle başlayalım:
using System;
using System.Collections.Generic;
using System.Linq;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Önceki programın metnine bakın. Tanıdık öğeleri tanırsınız. Metnin tamamı tek bir kaynak dosyayı veya bir derleme birimini temsil eder. Bu kaynak dosyanın ilk üç satırı yönergelerini kullanıyor. Kalan kaynak bir ad alanı bildiriminde yer alır. Ad alanı bildirimi bir alt sınıf bildirimi içerir. Sınıf bildirimi bir yöntem bildirimi içerir.
Söz Dizimi API'sinde, derleme birimini temsil eden kök ile bir ağaç yapısı oluşturulur. Ağaçtaki düğümler using yönergelerini, ad alanı bildirimini ve programın diğer tüm öğelerini temsil eder. Ağaç yapısı en düşük düzeylere kadar devam eder: "Merhaba Dünya!" dizesi, bir bağımsız değişkenin alt dizesi olan bir dize değişmez belirtecidir. Söz Dizimi API'si, programın yapısına erişim sağlar. Belirli kod uygulamalarını sorgulayabilir, kodu anlamak için tüm ağaçta gezinebilir ve var olan ağacı değiştirerek yeni ağaçlar oluşturabilirsiniz.
Bu kısa açıklama Söz Dizimi API'sini kullanarak erişilebilen bilgi türlerine genel bir bakış sağlar. Söz Dizimi API'si, C# ile bildiğiniz tanıdık kod yapılarını açıklayan resmi bir API'den başka bir şey değildir. Tüm özellikler satır sonları, boşluk ve girintileme dahil olmak üzere kodun nasıl biçimlendirildiğini içerir. Bu bilgileri kullanarak, kodu insan programcılar veya derleyici tarafından yazılan ve okunan şekilde tam olarak temsil edebilirsiniz. Bu yapıyı kullanmak, kaynak kodla son derece anlamlı bir düzeyde etkileşim kurmanızı sağlar. Artık metin dizeleri değil, C# programının yapısını temsil eden verilerdir.
Başlamak için .NET Compiler Platform SDK'sını yüklemeniz gerekir:
Yükleme yönergeleri - Visual Studio Yükleyicisi
Visual Studio Yükleyicisi .NET Compiler Platform SDK'sını bulmanın iki farklı yolu vardır:
Visual Studio Yükleyicisi - İş yükleri görünümünü kullanarak yükleme
.NET Compiler Platform SDK,Visual Studio uzantısı geliştirme iş yükünün bir parçası olarak otomatik olarak seçilmez. bunu isteğe bağlı bir bileşen olarak seçmelisiniz.
- Visual Studio Yükleyicisi çalıştırma
- Değiştir'i seçin
- Visual Studio uzantısı geliştirme iş yükünü denetleyin.
- Özet ağacında Visual Studio uzantısı geliştirme düğümünü açın.
- .NET Compiler Platform SDK kutusunu işaretleyin. Bunu en son isteğe bağlı bileşenler altında bulabilirsiniz.
İsteğe bağlı olarak , DGML düzenleyicisinin görselleştiricide grafikleri görüntülemesini de istersiniz:
- Özet ağacında Tek tek bileşenler düğümünü açın.
- DGML düzenleyicisi kutusunu işaretleyin
Visual Studio Yükleyicisi - Bağımsız bileşenler sekmesini kullanarak yükleme
- Visual Studio Yükleyicisi çalıştırma
- Değiştir'i seçin
- Tek tek bileşenler sekmesini seçin
- .NET Compiler Platform SDK kutusunu işaretleyin. Bunu derleyiciler, derleme araçları ve çalışma zamanları bölümünün altında en üstte bulabilirsiniz.
İsteğe bağlı olarak , DGML düzenleyicisinin görselleştiricide grafikleri görüntülemesini de istersiniz:
- DGML düzenleyicisi kutusunu işaretleyin. Kod araçları bölümünde bulabilirsiniz.
Söz dizimi ağaçlarını anlama
Söz Dizimi API'sini, C# kodunun yapısının herhangi bir çözümlemesi için kullanırsınız. Söz Dizimi API'si, söz dizimi ağaçlarını çözümlemek ve oluşturmak için ayrıştırıcıları, söz dizimi ağaçlarını ve yardımcı programları kullanıma sunar. Belirli söz dizimi öğeleri için kod arama veya bir programın kodunu okuma yöntemidir.
Söz dizimi ağacı, C# ve Visual Basic derleyicileri tarafından C# ve Visual Basic programlarını anlamak için kullanılan bir veri yapısıdır. Söz dizimi ağaçları, bir proje oluşturulduğunda veya bir geliştirici F5'e ulaştığında çalışan aynı ayrıştırıcı tarafından oluşturulur. Söz dizimi ağaçlarının dili tam uygunluktadır; bir kod dosyasındaki her bilgi biti ağaçta temsil edilir. Metne söz dizimi ağacı yazıldığında, ayrıştırılan tam özgün metin yeniden üretilir. Söz dizimi ağaçları da sabittir; bir söz dizimi ağacı oluşturulduktan sonra hiçbir zaman değiştirilemez. Ağaçların tüketicileri, kilitler veya diğer eşzamanlılık önlemleri olmadan, verilerin hiçbir zaman değişmediğinden haberdar olarak, birden çok iş parçacığındaki ağaçları analiz edebilir. Mevcut bir ağacı değiştirmenin sonucu olan yeni ağaçlar oluşturmak için API'leri kullanabilirsiniz.
Söz dizimi ağaçlarının dört birincil yapı taşları şunlardır:
- Microsoft.CodeAnalysis.SyntaxTree sınıfı, örneği ayrıştırma ağacının tamamını temsil eder. SyntaxTree dile özgü türevleri olan soyut bir sınıftır. C# (veya Visual Basic) içindeki metni ayrıştırmak için (veya Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree) sınıfının ayrıştırma yöntemlerini Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree kullanırsınız.
- Microsoft.CodeAnalysis.SyntaxNode örnekleri bildirimleri, deyimleri, yan tümceleri ve ifadeler gibi söz dizimsel yapıları temsil eden sınıfı.
- Tek Microsoft.CodeAnalysis.SyntaxToken bir anahtar sözcüğü, tanımlayıcıyı, işleci veya noktalamayı temsil eden yapı.
- Ve son olarak Microsoft.CodeAnalysis.SyntaxTrivia , belirteçler, önişlem yönergeleri ve açıklamalar arasındaki boşluk gibi söz dizimsel olarak önemsiz bilgi bitlerini temsil eden yapı.
Trivia, belirteçler ve düğümler, Visual Basic veya C# kodunun bir parçasındaki her şeyi tamamen temsil eden bir ağaç oluşturmak için hiyerarşik olarak oluşturulur. Söz Dizimi Görselleştiricisi penceresini kullanarak bu yapıyı görebilirsiniz. Visual Studio'daDiğer Windows>Söz Dizimi GörselleştiricisiniGörüntüle'yi> seçin. Örneğin, Söz Dizimi Görselleştiricisi kullanılarak incelenen önceki C# kaynak dosyası aşağıdaki şekilde görünür:
Söz Dizimi Düğümü: Mavi | SyntaxToken: Yeşil | SyntaxTrivia: Red
Bu ağaç yapısında gezinerek kod dosyasında herhangi bir deyimi, ifadeyi, belirteci veya boşluk bitini bulabilirsiniz.
Söz Dizimi API'lerini kullanarak kod dosyasında herhangi bir şey bulabilseniz de, çoğu senaryo küçük kod parçacıklarının incelenmesini ya da belirli deyimleri veya parçaları aramayı içerir. Aşağıdaki iki örnek, kodun yapısına göz atmak veya tek deyimleri aramak için tipik kullanımları gösterir.
Çapraz geçiş ağaçları
Söz dizimi ağacındaki düğümleri iki şekilde inceleyebilirsiniz. Her düğümü incelemek için ağaçtan geçiş yapabilir veya belirli öğeleri veya düğümleri sorgulayabilirsiniz.
El ile dolaşma
Bu örneğin tamamlanmış kodunu GitHub depomuzda görebilirsiniz.
Not
Söz Dizimi Ağacı türleri, programın farklı konumlarında geçerli olan farklı söz dizimi öğelerini açıklamak için devralmayı kullanır. Bu API'lerin kullanılması genellikle özellikleri veya koleksiyon üyelerini belirli türetilmiş türlere atama anlamına gelir. Aşağıdaki örneklerde atama ve atamalar, açıkça yazılan değişkenleri kullanan ayrı deyimlerdir. API'nin dönüş türlerini ve döndürülen nesnelerin çalışma zamanı türünü görmek için kodu okuyabilirsiniz. Pratikte, örtük olarak yazılan değişkenleri kullanmak ve incelenen nesne türünü açıklamak için API adlarına güvenmek daha yaygındır.
Yeni bir C# Tek Başına Kod Çözümleme Aracı projesi oluşturun:
- Visual Studio'daDosya Yeni Proje'yiseçerek> Yeni >Proje iletişim kutusunu görüntüleyin.
- Visual C#>Genişletilebilirlik'in altında Tek Başına Kod Çözümleme Aracı'nı seçin.
- Projenizi "SyntaxTreeManualTraversal" olarak adlandırıp Tamam'a tıklayın.
Daha önce gösterilen temel "Merhaba Dünya!" programını analiz edeceksiniz.
Merhaba Dünya programının metnini sınıfınızda Program
sabit olarak ekleyin:
const string programText =
@"using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}";
Ardından, sabitteki kod metninin programText
söz dizimi ağacını oluşturmak için aşağıdaki kodu ekleyin. Yönteminize Main
aşağıdaki satırı ekleyin:
SyntaxTree tree = CSharpSyntaxTree.ParseText(programText);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
Bu iki satır ağacı oluşturur ve bu ağacın kök düğümünü alır. Artık ağaçtaki düğümleri inceleyebilirsiniz. Ağaçtaki kök düğümün özelliklerinden bazılarını görüntülemek için yönteminize Main
şu satırları ekleyin:
WriteLine($"The tree is a {root.Kind()} node.");
WriteLine($"The tree has {root.Members.Count} elements in it.");
WriteLine($"The tree has {root.Usings.Count} using statements. They are:");
foreach (UsingDirectiveSyntax element in root.Usings)
WriteLine($"\t{element.Name}");
Kodunuzun bu ağaçtaki kök düğüm hakkında ne keşfettiğini görmek için uygulamayı çalıştırın.
Genellikle kod hakkında bilgi edinmek için ağaçtan geçiş yapılır. Bu örnekte, API'leri keşfetmek için bildiğiniz kodu analiz ediyorsunuz. Düğümün ilk üyesini root
incelemek için aşağıdaki kodu ekleyin:
MemberDeclarationSyntax firstMember = root.Members[0];
WriteLine($"The first member is a {firstMember.Kind()}.");
var helloWorldDeclaration = (NamespaceDeclarationSyntax)firstMember;
Bu üye bir Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax. Bildirim kapsamındaki namespace HelloWorld
her şeyi temsil eder. Ad alanında HelloWorld
bildirilen düğümleri incelemek için aşağıdaki kodu ekleyin:
WriteLine($"There are {helloWorldDeclaration.Members.Count} members declared in this namespace.");
WriteLine($"The first member is a {helloWorldDeclaration.Members[0].Kind()}.");
Öğrendiklerini görmek için programı çalıştırın.
Bildiriminin bir Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntaxolduğunu bildiğinize göre, sınıf bildirimini incelemek için bu türdeki yeni bir değişken bildirin. Bu sınıf yalnızca bir üye içerir: Main
yöntemi. yöntemini bulmak Main
için aşağıdaki kodu ekleyin ve öğesine Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntaxyayınlayın.
var programDeclaration = (ClassDeclarationSyntax)helloWorldDeclaration.Members[0];
WriteLine($"There are {programDeclaration.Members.Count} members declared in the {programDeclaration.Identifier} class.");
WriteLine($"The first member is a {programDeclaration.Members[0].Kind()}.");
var mainDeclaration = (MethodDeclarationSyntax)programDeclaration.Members[0];
Yöntem bildirimi düğümü, yöntemle ilgili tüm söz dizimsel bilgileri içerir. Yönteminin dönüş türünü Main
, bağımsız değişkenlerin sayısını ve türlerini ve yönteminin gövde metnini görüntüleyelim. Şu kodu ekleyin:
WriteLine($"The return type of the {mainDeclaration.Identifier} method is {mainDeclaration.ReturnType}.");
WriteLine($"The method has {mainDeclaration.ParameterList.Parameters.Count} parameters.");
foreach (ParameterSyntax item in mainDeclaration.ParameterList.Parameters)
WriteLine($"The type of the {item.Identifier} parameter is {item.Type}.");
WriteLine($"The body text of the {mainDeclaration.Identifier} method follows:");
WriteLine(mainDeclaration.Body?.ToFullString());
var argsParameter = mainDeclaration.ParameterList.Parameters[0];
Bu program hakkında keşfettiğiniz tüm bilgileri görmek için programı çalıştırın:
The tree is a CompilationUnit node.
The tree has 1 elements in it.
The tree has 4 using statements. They are:
System
System.Collections
System.Linq
System.Text
The first member is a NamespaceDeclaration.
There are 1 members declared in this namespace.
The first member is a ClassDeclaration.
There are 1 members declared in the Program class.
The first member is a MethodDeclaration.
The return type of the Main method is void.
The method has 1 parameters.
The type of the args parameter is string[].
The body text of the Main method follows:
{
Console.WriteLine("Hello, World!");
}
Sorgu yöntemleri
Ağaçlardan geçiş yapmaya ek olarak, üzerinde Microsoft.CodeAnalysis.SyntaxNodetanımlanan sorgu yöntemlerini kullanarak söz dizimi ağacını da keşfedebilirsiniz. Bu yöntemler XPath hakkında bilgi sahibi olan herkes tarafından hemen bilinmelidir. Bir ağaçtaki öğeleri hızla bulmak için LINQ ile bu yöntemleri kullanabilirsiniz. , SyntaxNodeAncestorsAndSelf ve ChildNodesgibi DescendantNodessorgu yöntemlerine sahiptir.
Ağaçta gezinmeye alternatif olarak yöntemin bağımsız değişkenini Main
bulmak için bu sorgu yöntemlerini kullanabilirsiniz. Aşağıdaki kodu yönteminizin Main
altına ekleyin:
var firstParameters = from methodDeclaration in root.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
where methodDeclaration.Identifier.ValueText == "Main"
select methodDeclaration.ParameterList.Parameters.First();
var argsParameter2 = firstParameters.Single();
WriteLine(argsParameter == argsParameter2);
İlk deyim, önceki örnektekiyle DescendantNodes aynı parametreyi bulmak için bir LINQ ifadesi ve yöntemi kullanır.
Programı çalıştırdığınızda LINQ ifadesinin ağaçta el ile gezinmeyle aynı parametreyi bulduğunu görebilirsiniz.
Örnek, çapraz geçiş yapılırken söz dizimi ağaçları hakkındaki bilgileri görüntülemek için deyimleri kullanır WriteLine
. Ayrıca, hata ayıklayıcı altında tamamlanmış programı çalıştırarak daha fazla bilgi edinebilirsiniz. Merhaba dünya programı için oluşturulan söz dizimi ağacının parçası olan özelliklerin ve yöntemlerin daha fazlasını inceleyebilirsiniz.
Söz dizimi yürütücüleri
Genellikle söz dizimi ağacında belirli bir türdeki tüm düğümleri bulmak istersiniz, örneğin, bir dosyadaki her özellik bildirimi. sınıfını Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker genişleterek ve yöntemini geçersiz kılarak VisitPropertyDeclaration(PropertyDeclarationSyntax) , bir söz dizimi ağacındaki her özellik bildirimini yapısını önceden bilmeden işlersiniz. CSharpSyntaxWalker bir düğümü ve alt öğelerini özyinelemeli olarak ziyaret eden belirli bir türdür CSharpSyntaxVisitor .
Bu örnek, söz CSharpSyntaxWalker dizimi ağacını inceleyen bir uygular. Ad alanını içeri aktarmadan System
bulduğu yönergeleri toplarusing
.
Yeni bir C# Tek Başına Kod Çözümleme Aracı projesi oluşturun; "SyntaxWalker" olarak adlandır.
Bu örneğin tamamlanmış kodunu GitHub depomuzda görebilirsiniz. GitHub'daki örnek, bu öğreticide açıklanan her iki projeyi de içerir.
Önceki örnekte olduğu gibi, analiz etmek istediğiniz programın metnini tutmak için bir dize sabiti tanımlayabilirsiniz:
const string programText =
@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace TopLevel
{
using Microsoft;
using System.ComponentModel;
namespace Child1
{
using Microsoft.Win32;
using System.Runtime.InteropServices;
class Foo { }
}
namespace Child2
{
using System.CodeDom;
using Microsoft.CSharp;
class Bar { }
}
}";
Bu kaynak metin dört farklı konuma dağılmış yönergeler içerir using
: dosya düzeyi, üst düzey ad alanında ve iç içe iki ad alanında. Bu örnekte, kodu sorgulamak için sınıfını kullanmaya yönelik CSharpSyntaxWalker temel bir senaryo vurgulanır. Bildirimleri kullanarak bulmak için kök söz dizimi ağacındaki her düğümü ziyaret etmek zahmetli olabilir. Bunun yerine türetilmiş bir sınıf oluşturur ve yalnızca ağaçtaki geçerli düğüm bir using yönergesi olduğunda çağrılan yöntemi geçersiz kılarsınız. Ziyaretçiniz diğer düğüm türleri üzerinde herhangi bir çalışma yapmaz. Bu tek yöntem deyimlerin her birini using
inceler ve ad alanında olmayan ad alanlarının System
bir koleksiyonunu oluşturur. Tüm using
deyimleri ancak yalnızca using
deyimlerini inceleyen bir CSharpSyntaxWalker oluşturursunuz.
Artık program metnini tanımladığınıza göre, bir SyntaxTree
oluşturmanız ve bu ağacın kökünü almanız gerekir:
SyntaxTree tree = CSharpSyntaxTree.ParseText(programText);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
Ardından yeni bir sınıf oluşturun. Visual Studio'da Proje>Yeni Öğe Ekle'yi seçin. Yeni Öğe Ekle iletişim kutusunda, dosya adı olarak UsingCollector.cs yazın.
Sınıfta ziyaretçi işlevini UsingCollector
uygularsınızusing
. sınıfını UsingCollector
öğesinden CSharpSyntaxWalkertüreterek başlayın.
class UsingCollector : CSharpSyntaxWalker
Topladığınız ad alanı düğümlerini barındırmak için depolamaya ihtiyacınız vardır. sınıfında genel salt okunur bir özellik UsingCollector
bildirin; bulduğunuz düğümleri depolamak UsingDirectiveSyntax için bu değişkeni kullanırsınız:
public ICollection<UsingDirectiveSyntax> Usings { get; } = new List<UsingDirectiveSyntax>();
Temel sınıfı, CSharpSyntaxWalker söz dizimi ağacındaki her düğümü ziyaret etme mantığını uygular. Türetilmiş sınıf, ilgilendiğiniz belirli düğümler için çağrılan yöntemleri geçersiz kılar. Bu durumda, herhangi bir using
yönergeyle ilgileniyorsunuz. Bu, yöntemini geçersiz kılmanız VisitUsingDirective(UsingDirectiveSyntax) gerektiği anlamına gelir. Bu yöntemin tek bağımsız değişkeni bir Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax nesnedir. Bu, ziyaretçileri kullanmanın önemli bir avantajıdır: geçersiz kılınan yöntemleri belirli bir düğüm türüne önceden oluşturulmuş bağımsız değişkenlerle çağırırlar. sınıfı, Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax içeri aktarılan ad alanının adını depolayan bir Name özelliğe sahiptir. Bu bir Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax. Geçersiz kılmaya VisitUsingDirective(UsingDirectiveSyntax) aşağıdaki kodu ekleyin:
public override void VisitUsingDirective(UsingDirectiveSyntax node)
{
WriteLine($"\tVisitUsingDirective called with {node.Name}.");
if (node.Name.ToString() != "System" &&
!node.Name.ToString().StartsWith("System."))
{
WriteLine($"\t\tSuccess. Adding {node.Name}.");
this.Usings.Add(node);
}
}
Önceki örnekte olduğu gibi, bu yöntemin anlaşılmasına yardımcı olmak için çeşitli WriteLine
deyimler eklediniz. Çağrıldığını ve her seferinde hangi bağımsız değişkenlerin geçirildiğini görebilirsiniz.
Son olarak, oluşturmak UsingCollector
için iki kod satırı eklemeniz ve kök düğümü ziyaret ederek tüm using
deyimleri toplamanız gerekir. Ardından, toplayıcınızın bulduğu tüm using
deyimleri görüntülemek için bir foreach
döngü ekleyin:
var collector = new UsingCollector();
collector.Visit(root);
foreach (var directive in collector.Usings)
{
WriteLine(directive.Name);
}
Programı derleyin ve çalıştırın. Aşağıdaki çıkışı görmeniz gerekir:
VisitUsingDirective called with System.
VisitUsingDirective called with System.Collections.Generic.
VisitUsingDirective called with System.Linq.
VisitUsingDirective called with System.Text.
VisitUsingDirective called with Microsoft.CodeAnalysis.
Success. Adding Microsoft.CodeAnalysis.
VisitUsingDirective called with Microsoft.CodeAnalysis.CSharp.
Success. Adding Microsoft.CodeAnalysis.CSharp.
VisitUsingDirective called with Microsoft.
Success. Adding Microsoft.
VisitUsingDirective called with System.ComponentModel.
VisitUsingDirective called with Microsoft.Win32.
Success. Adding Microsoft.Win32.
VisitUsingDirective called with System.Runtime.InteropServices.
VisitUsingDirective called with System.CodeDom.
VisitUsingDirective called with Microsoft.CSharp.
Success. Adding Microsoft.CSharp.
Microsoft.CodeAnalysis
Microsoft.CodeAnalysis.CSharp
Microsoft
Microsoft.Win32
Microsoft.CSharp
Press any key to continue . . .
Tebrikler! C# kaynak kodunda belirli C# deyim ve bildirim türlerini bulmak için Sözdizimi API'sini kullandınız.