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 öğretici, Söz dizimi analizini kullanmaya başlama ve Anlam analizini kullanmaya başlama hızlı başlangıçlarında keşfedilen kavramlar ve teknikler üzerine oluşturulmuştır. Henüz yapmadıysanız, bu hızlı başlangıçları buna başlamadan önce tamamlamanız gerekir.
Bu hızlı başlangıçta söz dizimi ağaçlarını oluşturma ve dönüştürme tekniklerini keşfedersiniz. Önceki hızlı başlangıçlarda öğrendiğiniz tekniklerle birlikte ilk komut satırı yeniden düzenlemenizi oluşturursunuz!
Yükleme yönergeleri - Visual Studio Yükleyicisi
Visual Studio Yükleyicisi .NET Derleyici Platformu 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 Derleyici Platformu SDK'sı, Visual Studio uzantısı geliştirme iş yükünün bir parçası olarak otomatik olarak seçilmez. İsteğ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 Derleyici Platformu SDK'sı kutusunu işaretleyin. Bunu isteğe bağlı bileşenler altında son olarak bulacaksınız.
İ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 - Tek 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 Derleyici Platformu SDK'sı kutusunu işaretleyin. 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.
Değişmezlik ve .NET derleyici platformu
Değişmezlik , .NET derleyici platformunun temel bir tenet'idir. Sabit veri yapıları oluşturulduktan sonra değiştirilemez. Sabit veri yapıları aynı anda birden çok tüketici tarafından güvenli bir şekilde paylaşılabilir ve analiz edilebilir. Bir tüketicinin tahmin edilemeyen yollarla başka bir tüketiciyi etkilemesi tehlikesi yoktur. Çözümleyicinizin kilitlere veya diğer eşzamanlılık ölçülerine ihtiyacı yoktur. Bu kural söz dizimi ağaçları, derlemeler, simgeler, anlamsal modeller ve karşılaştığınız diğer tüm veri yapıları için geçerlidir. API'ler, mevcut yapıları değiştirmek yerine, eskilerle belirtilen farkları temel alarak yeni nesneler oluşturur. Dönüştürmeleri kullanarak yeni ağaçlar oluşturmak için söz dizimi ağaçlarına bu kavramı uygularsınız.
Ağaç oluşturma ve dönüştürme
Söz dizimi dönüştürmeleri için iki stratejiden birini seçersiniz. Fabrika yöntemleri, değiştireceğiniz belirli düğümleri veya yeni kod eklemek istediğiniz belirli konumları ararken en iyi şekilde kullanılır. Yeniden yazanlar , değiştirmek istediğiniz kod desenleri için projenin tamamını taramak istediğinizde en iyisidir.
Fabrika yöntemleriyle düğüm oluşturma
İlk söz dizimi dönüştürmesinde fabrika yöntemleri gösterilir. Deyimini deyimiyle using System.Collections;
using System.Collections.Generic;
değiştireceksiniz. Bu örnekte, fabrika yöntemlerini kullanarak Microsoft.CodeAnalysis.CSharp.SyntaxFactory nesneleri nasıl oluşturacağınız Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode gösterilmektedir. Her düğüm, belirteç veya trivia türü için, bu tür bir örnek oluşturan bir fabrika yöntemi vardır. Düğümleri hiyerarşik olarak aşağıdan yukarıya doğru oluşturarak söz dizimi ağaçları oluşturursunuz. Ardından, mevcut düğümleri oluşturduğunuz yeni ağaçla değiştirerek mevcut programı dönüştüreceksiniz.
Visual Studio'yu başlatın ve yeni bir C# Tek Başına Kod Çözümleme Aracı projesi oluşturun. Visual Studio'da Dosya Yeni Proje'yi seç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. Bu hızlı başlangıçta iki örnek proje vardır; bu nedenle çözüme SyntaxTransformationQuickStart adını verin ve projeyi ConstructionCS olarak adlandırın. Tamam'a tıklayın.
Bu proje, ad alanını temsil eden System.Collections.Generic
bir Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax oluşturmak için sınıf yöntemlerini kullanırMicrosoft.CodeAnalysis.CSharp.SyntaxFactory.
aşağıdaki using yönergesini öğesinin Program.cs
en üstüne ekleyin.
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static System.Console;
deyimini temsil eden ağacı oluşturmak için ad söz dizimi düğümleriusing System.Collections.Generic;
oluşturacaksınız. NameSyntax , C# dilinde görünen dört ad türünün temel sınıfıdır. C# dilinde görünebilecek herhangi bir ad oluşturmak için bu dört ad türünü birlikte oluşturursunuz:
- Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax, ve
Microsoft
gibiSystem
basit tek tanımlayıcı adlarını temsil eder. - Microsoft.CodeAnalysis.CSharp.Syntax.GenericNameSyntaxgibi
List<int>
genel bir türü veya yöntem adını temsil eder. - Microsoft.CodeAnalysis.CSharp.Syntax.QualifiedNameSyntaxgibi formun
<left-name>.<right-identifier-or-generic-name>
System.IO
tam adını temsil eder. - Microsoft.CodeAnalysis.CSharp.Syntax.AliasQualifiedNameSyntaxgibi
LibraryV2::Foo
bir derleme extern diğer adı kullanan bir adı temsil eder.
Bir düğüm oluşturmak NameSyntax için yöntemini kullanırsınızIdentifierName(String). yönteminize Main
Program.cs
aşağıdaki kodu ekleyin:
NameSyntax name = IdentifierName("System");
WriteLine($"\tCreated the identifier {name}");
Yukarıdaki kod bir IdentifierNameSyntax nesnesi oluşturur ve değişkenine name
atar. Roslyn API'lerinin çoğu, ilgili türlerle çalışmayı kolaylaştırmak için temel sınıfları döndürür. name
değişkenini NameSyntaxoluştururken QualifiedNameSyntaxyeniden kullanılabilir. Örneği oluştururken tür çıkarımı kullanmayın. Bu projede bu adımı otomatikleştireceksiniz.
Adı oluşturdunuz. Şimdi, bir oluşturarak QualifiedNameSyntaxağaçta daha fazla düğüm oluşturmanın zamanı geldi. Yeni ağaç adın solu olarak, ad alanı için Collections
ise sağ tarafı QualifiedNameSyntaxolarak yeni IdentifierNameSyntax bir ad alanı kullanırname
. aşağıdaki kodu içine program.cs
ekleyin:
name = QualifiedName(name, IdentifierName("Collections"));
WriteLine(name.ToString());
Kodu yeniden çalıştırın ve sonuçları görün. Kodu temsil eden bir düğüm ağacı oluşturuyorsunuz. Ad alanı System.Collections.Generic
için derlemek QualifiedNameSyntax için bu desene devam edersiniz. aşağıdaki kodu içine Program.cs
ekleyin:
name = QualifiedName(name, IdentifierName("Generic"));
WriteLine(name.ToString());
Eklenecek kodun ağacını oluşturduğunuzu görmek için programı yeniden çalıştırın.
Değiştirilmiş ağaç oluşturma
Tek bir deyim içeren küçük bir söz dizimi ağacı oluşturdunuz. Yeni düğümler oluşturmak için API'ler, tek deyimler veya diğer küçük kod blokları oluşturmak için doğru seçimdir. Ancak, daha büyük kod blokları oluşturmak için düğümleri değiştiren veya var olan bir ağaca düğüm ekleyen yöntemleri kullanmanız gerekir. Söz dizimi ağaçlarının sabit olduğunu unutmayın. Söz Dizimi API'si, oluşturma sonrasında mevcut söz dizimi ağacını değiştirmek için herhangi bir mekanizma sağlamaz. Bunun yerine, mevcut ağaçlarda yapılan değişikliklere göre yeni ağaç üreten yöntemler sağlar. With*
yöntemleri, sınıfında bildirilen veya uzantı yöntemlerinden SyntaxNode türetilen somut sınıflarda SyntaxNodeExtensions tanımlanır. Bu yöntemler, mevcut düğümün alt özelliklerine değişiklikler uygulayarak yeni bir düğüm oluşturur. Ayrıca, ReplaceNode uzantı yöntemi bir alt ağaçtaki alt düğümü değiştirmek için kullanılabilir. Bu yöntem ayrıca üst öğeyi yeni oluşturulan alt öğeye işaret etmek üzere güncelleştirir ve bu işlemi tüm ağaçta yineler. Bu işlem, ağacı yeniden sabitleme olarak bilinen bir işlemdir.
Sonraki adım, bir programın tamamını (küçük) temsil eden bir ağaç oluşturmak ve sonra bunu değiştirmektir. Sınıfın başına Program
aşağıdaki kodu ekleyin:
private const string sampleCode =
@"using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}";
Dekont
Örnek kod, ad alanını System.Collections
değil ad alanını System.Collections.Generic
kullanır.
Ardından, aşağıdaki kodu yöntemin Main
altına ekleyerek metni ayrıştırın ve bir ağaç oluşturun:
SyntaxTree tree = CSharpSyntaxTree.ParseText(sampleCode);
var root = (CompilationUnitSyntax)tree.GetRoot();
Bu örnek, bir UsingDirectiveSyntax düğümdeki adı önceki kodda yapılan adla değiştirmek için yöntemini kullanırWithName(NameSyntax).
Yöntemini kullanarak yeni UsingDirectiveSyntax bir düğüm oluşturarak WithName(NameSyntax) adı önceki kodda oluşturduğunuz adla güncelleştirin System.Collections
. Yönteminin en altına Main
aşağıdaki kodu ekleyin:
var oldUsing = root.Usings[1];
var newUsing = oldUsing.WithName(name);
WriteLine(root.ToString());
Programı çalıştırın ve çıkışa dikkatlice bakın. Kök newUsing
ağaca yerleştirilmemiş. Özgün ağaç değiştirilmedi.
Yeni bir ağaç oluşturmak için uzantı yöntemini kullanarak ReplaceNode aşağıdaki kodu ekleyin. Yeni ağaç, mevcut içeri aktarma işlemini güncelleştirilmiş newUsing
düğümle değiştirmenin sonucudur. Bu yeni ağacı mevcut root
değişkene atarsınız:
root = root.ReplaceNode(oldUsing, newUsing);
WriteLine(root.ToString());
Programı yeniden çalıştırın. Bu kez ağaç artık ad alanını System.Collections.Generic
doğru bir şekilde içeri aktarır.
Kullanarak ağaçları dönüştürme SyntaxRewriters
ve ReplaceNode yöntemleri, With*
söz dizimi ağacının tek tek dallarını dönüştürmek için kullanışlı araçlar sağlar. sınıfı, Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter söz dizimi ağacında birden çok dönüştürme gerçekleştirir. Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter sınıfı, öğesinin bir alt sınıfıdırMicrosoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>. , CSharpSyntaxRewriter belirli bir türüne SyntaxNodebir dönüştürme uygular. Bir söz dizimi ağacında göründükleri her yerde, birden çok nesne türüne SyntaxNode dönüştürme uygulayabilirsiniz. Bu hızlı başlangıçtaki ikinci proje, tür çıkarımının kullanabildiği her yerde yerel değişken bildirimlerindeki açık türleri kaldıran bir komut satırı yeniden düzenlemesi oluşturur.
Yeni bir C# Tek Başına Kod Çözümleme Aracı projesi oluşturun. Visual Studio'da çözüm düğümüne SyntaxTransformationQuickStart
sağ tıklayın. Yeni Proje Ekle'yi>seç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 TransformationCS
adlandırıp Tamam'a tıklayın.
İlk adım, dönüştürmelerinizi gerçekleştirmek için öğesinden CSharpSyntaxRewriter türetilen bir sınıf oluşturmaktır. Projeye yeni bir sınıf dosyası ekleyin. Visual Studio'da Proje>Sınıf Ekle...'yi seçin.Yeni Öğe Ekle iletişim kutusunda dosya adı olarak yazınTypeInferenceRewriter.cs
.
Aşağıdaki using yönergelerini TypeInferenceRewriter.cs
dosyasına ekleyin:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Ardından sınıfın sınıfını TypeInferenceRewriter
genişletmesini CSharpSyntaxRewriter sağlayın:
public class TypeInferenceRewriter : CSharpSyntaxRewriter
bir tutmak ve oluşturucuda başlatmak üzere özel bir SemanticModel salt okunur alan bildirmek için aşağıdaki kodu ekleyin. Tür çıkarımının nerede kullanılabileceğini belirlemek için bu alana daha sonra ihtiyacınız olacak:
private readonly SemanticModel SemanticModel;
public TypeInferenceRewriter(SemanticModel semanticModel) => SemanticModel = semanticModel;
VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax) Yöntemini geçersiz kılın:
public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
{
}
Dekont
Roslyn API'lerinin çoğu, döndürülen gerçek çalışma zamanı türlerinin temel sınıfları olan dönüş türlerini bildirir. Birçok senaryoda, bir düğüm türü tamamen başka bir düğüm türüyle değiştirilebilir ve hatta kaldırılabilir. Bu örnekte yöntemi, VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax) türetilmiş türü LocalDeclarationStatementSyntaxyerine bir SyntaxNodedöndürür. Bu yeniden yazan, mevcut düğüme göre yeni LocalDeclarationStatementSyntax bir düğüm döndürür.
Bu hızlı başlangıçta yerel değişken bildirimleri işlenir. Döngüler, döngüler, for
LINQ ifadeleri ve lambda ifadeleri gibi foreach
diğer bildirimlere genişletebilirsiniz. Ayrıca bu yeniden yazan yalnızca en basit formun bildirimlerini dönüştürür:
Type variable = expression;
Kendi başınıza keşfetmek istiyorsanız, bu tür değişken bildirimleri için tamamlanmış örneği genişletmeyi göz önünde bulundurun:
// Multiple variables in a single declaration.
Type variable1 = expression1,
variable2 = expression2;
// No initializer.
Type variable;
Bu bildirim biçimlerini yeniden yazmayı atlamak için yönteminin VisitLocalDeclarationStatement
gövdesine aşağıdaki kodu ekleyin:
if (node.Declaration.Variables.Count > 1)
{
return node;
}
if (node.Declaration.Variables[0].Initializer == null)
{
return node;
}
yöntemi, parametresi değiştirilmemiş olarak döndürülerek node
yeniden yazma gerçekleştirilmediğini gösterir. Bu if
ifadelerden hiçbiri doğru değilse, düğüm başlatma ile olası bir bildirimi temsil eder. Bildirimde belirtilen tür adını ayıklamak ve bir tür simgesi almak için alanını kullanarak SemanticModel bağlamak için şu deyimleri ekleyin:
var declarator = node.Declaration.Variables.First();
var variableTypeName = node.Declaration.Type;
var variableType = (ITypeSymbol)SemanticModel
.GetSymbolInfo(variableTypeName)
.Symbol;
Şimdi başlatıcı ifadesini bağlamak için şu deyimi ekleyin:
var initializerInfo = SemanticModel.GetTypeInfo(declarator.Initializer.Value);
Son olarak, başlatıcı ifadesinin türü belirtilen türle eşleşiyorsa mevcut tür adını anahtar sözcüğüyle var
değiştirmek için aşağıdaki if
deyimi ekleyin:
if (SymbolEqualityComparer.Default.Equals(variableType, initializerInfo.Type))
{
TypeSyntax varTypeName = SyntaxFactory.IdentifierName("var")
.WithLeadingTrivia(variableTypeName.GetLeadingTrivia())
.WithTrailingTrivia(variableTypeName.GetTrailingTrivia());
return node.ReplaceNode(variableTypeName, varTypeName);
}
else
{
return node;
}
Bildirim başlatıcı ifadesini bir temel sınıfa veya arabirime dönüştürebileceğinden koşullu gereklidir. Bu isteniyorsa, ödevin sol ve sağ tarafındaki türler eşleşmiyor. Bu durumlarda açık türün kaldırılması bir programın semantiğini değiştirir. var
, bağlamsal bir anahtar sözcük olduğundan var
anahtar sözcük yerine tanımlayıcı olarak belirtilir. Dikey boşluk ve girintiyi korumak için baştaki ve sondaki trivia (boşluk) eski tür adından var
anahtar sözcüğüne aktarılır. Tür adı aslında bildirim deyiminin torunları olduğundan, öğesini dönüştürmek LocalDeclarationStatementSyntax yerine With*
kullanmak ReplaceNode
daha kolaydır.
öğesini tamamladınız TypeInferenceRewriter
. Şimdi örneği tamamlamak için dosyanıza Program.cs
dönün. Bir test oluşturun ve bu testten Compilation alın SemanticModel . bunu SemanticModel kullanarak öğesini TypeInferenceRewriter
deneyin. En son bu adımı atacaksınız. Bu arada, test derlemenizi temsil eden bir yer tutucu değişken bildirin:
Compilation test = CreateTestCompilation();
Bir dakika duraklattıktan sonra, hiçbir yöntemin mevcut olmadığını CreateTestCompilation
bildiren bir hata dalgalı çizginin göründüğünü görmeniz gerekir. Ampulü açmak için Ctrl+Nokta tuşlarına basın ve ardından Yöntem Saptama Oluştur komutunu çağırmak için Enter tuşuna basın. Bu komut, sınıfındaki CreateTestCompilation
yöntemi için bir yöntem saptaması Program
oluşturur. Bu yöntemi daha sonra doldurmak için geri döneceksiniz:
Testteki Compilationher SyntaxTree birini yinelemek için aşağıdaki kodu yazın. Her biri için, bu ağaç için ile SemanticModel yeni TypeInferenceRewriter
bir başlatın:
foreach (SyntaxTree sourceTree in test.SyntaxTrees)
{
SemanticModel model = test.GetSemanticModel(sourceTree);
TypeInferenceRewriter rewriter = new TypeInferenceRewriter(model);
SyntaxNode newSource = rewriter.Visit(sourceTree.GetRoot());
if (newSource != sourceTree.GetRoot())
{
File.WriteAllText(sourceTree.FilePath, newSource.ToFullString());
}
}
Oluşturduğunuz deyiminin foreach
içine, her kaynak ağaçta dönüştürmeyi gerçekleştirmek için aşağıdaki kodu ekleyin. Bu kod, herhangi bir düzenleme yapıldıysa yeni dönüştürülmüş ağacı koşullu olarak yazar. Yeniden yazan, yalnızca tür çıkarımı kullanılarak basitleştirilebilen bir veya daha fazla yerel değişken bildirimiyle karşılaşırsa ağacı değiştirmelidir:
SyntaxNode newSource = rewriter.Visit(sourceTree.GetRoot());
if (newSource != sourceTree.GetRoot())
{
File.WriteAllText(sourceTree.FilePath, newSource.ToFullString());
}
Kodun altında File.WriteAllText
dalgalı çizgiler görmeniz gerekir. Ampulü seçin ve gerekli using System.IO;
deyimi ekleyin.
Neredeyse bitti! Bir adım kaldı: test Compilationoluşturma. Bu hızlı başlangıç sırasında tür çıkarımı hiç kullanmadığınızdan, bu mükemmel bir test çalışması olurdu. Ne yazık ki, C# proje dosyasından Derleme oluşturmak bu kılavuzun kapsamının dışındadır. Ama neyse ki, talimatları dikkatle takip ettiyseniz, umut var. yönteminin CreateTestCompilation
içeriğini aşağıdaki kodla değiştirin. Bu hızlı başlangıçta açıklanan projeyle çakışan bir test derlemesi oluşturur:
String programPath = @"..\..\..\Program.cs";
String programText = File.ReadAllText(programPath);
SyntaxTree programTree =
CSharpSyntaxTree.ParseText(programText)
.WithFilePath(programPath);
String rewriterPath = @"..\..\..\TypeInferenceRewriter.cs";
String rewriterText = File.ReadAllText(rewriterPath);
SyntaxTree rewriterTree =
CSharpSyntaxTree.ParseText(rewriterText)
.WithFilePath(rewriterPath);
SyntaxTree[] sourceTrees = { programTree, rewriterTree };
MetadataReference mscorlib =
MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
MetadataReference codeAnalysis =
MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
MetadataReference csharpCodeAnalysis =
MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location);
MetadataReference[] references = { mscorlib, codeAnalysis, csharpCodeAnalysis };
return CSharpCompilation.Create("TransformationCS",
sourceTrees,
references,
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
Parmaklarınızı çarpın ve projeyi çalıştırın. Visual Studio'da Hata AyıklamaYı>Başlat Hata Ayıklama'yı seçin. Visual Studio tarafından projenizdeki dosyaların değiştirildiği sorulmalıdır. Değiştirilen dosyaları yeniden yüklemek için "Tümüne Evet" seçeneğine tıklayın. Harikalığınızı gözlemlemek için onları inceleyin. Tüm bu açık ve yedekli tür tanımlayıcıları olmadan kodun ne kadar temiz göründüğüne dikkat edin.
Tebrikler! Derleyici API'lerini kullanarak C# projesindeki tüm dosyaları belirli söz dizimleri için arayan, bu desenlerle eşleşen kaynak kodun semantiğini analiz eden ve dönüştüren kendi yeniden düzenlemenizi yazdınız. Artık resmi olarak yeniden düzenleme yazarısınız!