Χρήση του Visual Studio και του Source Generator για αυτόματη δημιουργία κώδικα
Περιβάλλον λειτουργίας
- Οπτικό στούντιο
-
- Visual Studio 2022
- .ΔΊΧΤΥ
-
- .NET 8.0
Προϋποθέσεις
- Οπτικό στούντιο
-
- Λειτουργεί ακόμη και με μια κάπως παλαιότερη έκδοση
- .ΔΊΧΤΥ
-
- Λειτουργεί ακόμη και με μια κάπως παλαιότερη έκδοση
Αρχικά
Υπάρχουν διάφορες τεχνικές για την αυτόματη δημιουργία κώδικα με τους δικούς σας ορισμούς, αλλά σε αυτό το άρθρο θα σας δείξω πώς να χρησιμοποιήσετε μια γεννήτρια πηγής. Ένα από τα μεγαλύτερα πλεονεκτήματα μιας γεννήτριας πηγαίου κώδικα είναι ότι αναλύει τη δομή του πηγαίου κώδικα του τρέχοντος έργου και δημιουργεί νέο κώδικα βασισμένο σε αυτόν. Για παράδειγμα, όταν δημιουργείτε μια νέα κλάση, μπορείτε να την κάνετε έτσι ώστε ο κώδικας να προστίθεται αυτόματα ώστε να ταιριάζει με την κλάση. Μπορείτε να προγραμματίσετε το είδος του κώδικα που θέλετε να δημιουργήσετε, ώστε να μπορείτε να δημιουργήσετε οποιαδήποτε μορφή αυτόματης δημιουργίας κώδικα θέλετε.
Ο κώδικας ουσιαστικά δημιουργείται αυτόματα στο παρασκήνιο και ενσωματώνεται στο έργο πίσω από τα παρασκήνια. Δεδομένου ότι δεν εξάγεται ως αρχείο με ορατό τρόπο, δεν χρησιμοποιείται για τον σκοπό της επαναχρησιμοποίησης του κώδικα που δημιουργείται αυτόματα για γενικούς σκοπούς (αν και μπορεί να αφαιρεθεί αντιγράφοντάς τον προς το παρόν). Ωστόσο, δεδομένου ότι ο κώδικας δημιουργείται αυτόματα σύμφωνα με τη δομή του έργου, μειώνει το κόστος της χειροκίνητης εισαγωγής και μειώνει ανάλογα τα σφάλματα γραφής κώδικα, γεγονός που αποτελεί τεράστιο πλεονέκτημα.
Σε αυτό το άρθρο, θα εξηγήσω πώς να ελέγξετε ότι ο κώδικας δημιουργείται αυτόματα, οπότε δεν θα φτάσω στο σημείο να αναλύσω πραγματικά τον κώδικα βαθιά και να εκτελέσω προηγμένη έξοδο. Αναζητήστε το μόνοι σας ως εφαρμογή.
εγκατάστασης
Πρώτα, εγκαταστήστε το Visual Studio. Μια σύντομη εξήγηση συνοψίζεται στις ακόλουθες συμβουλές.
Βασικά, μπορείτε να το χρησιμοποιήσετε για οποιοδήποτε έργο, οπότε δεν έχει σημασία ποιος φόρτος εργασίας ρυθμίζετε. Ωστόσο, αυτή τη φορά, ως "μεμονωμένο στοιχείο", ". NET Compiler Platform SDK. Αυτό είναι χρήσιμο για τον εντοπισμό σφαλμάτων κατά την ανάπτυξη του Source Generator. Εάν έχετε ήδη εγκατεστημένο το Visual Studio, μπορείτε να το προσθέσετε από το μενού του Visual Studio στην περιοχή Εργαλεία > Λήψη εργαλείων και δυνατοτήτων.
Δημιουργία και προετοιμασία ενός έργου γεννήτριας πηγής
Το Source Generator δημιουργείται σε ένα έργο ξεχωριστό από το κύριο έργο εφαρμογής. Δεν έχει σημασία αν τα δημιουργήσετε πρώτα ή δημιουργήσετε επιπλέον αργότερα. Σε αυτήν την περίπτωση, θα το δημιουργήσω από το έργο Source Generator.
Στο Δημιουργία νέου έργου οθόνη, επιλέξτε Βιβλιοθήκη τάξης.
Το όνομα του έργου μπορεί να είναι οτιδήποτε, αλλά προς το παρόν CodeGenerator
, θα το αφήσουμε ως .
Για βιβλιοθήκες κλάσεων, η Γεννήτρια πηγής υποστηρίζει προς το παρόν . NET Πρότυπο 2.0.
Μόλις δημιουργήσετε το έργο σας, αποκτήστε το πακέτο με Microsoft.CodeAnalysis.CSharp
το NuGet.
Η συμπεριφορά μπορεί να διαφέρει ανάλογα με την έκδοση, αλλά δεν έχει νόημα να συνεχίσετε να χρησιμοποιείτε την παλιά έκδοση, οπότε θα βάλω την πιο πρόσφατη έκδοση.
Στη συνέχεια, ανοίξτε το αρχείο έργου ως κώδικα.
Όταν το ανοίξετε, θα δείτε τα εξής.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
</ItemGroup>
</Project>
Προσθέστε το ως εξής: Μη διστάσετε να προσθέσετε οτιδήποτε άλλο χρειάζεστε.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<!-- Roslyn によるコード解析を行う -->
<IsRoslynComponent>true</IsRoslynComponent>
<!-- 入れないと警告が表示されるので入れておく -->
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<!-- 最新の C# の記述を使いたいので入れておく -->
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
</ItemGroup>
</Project>
Στη συνέχεια, γράψτε τον κώδικα για την κλάση που θα δημιουργήσει αυτόματα τον κώδικα.
Πρώτα απ 'όλα, θα δημιουργήσουμε μόνο ένα πλαίσιο, οπότε ξαναγράψτε τον υπάρχοντα κώδικα από την αρχή Class1.cs
ή προσθέστε έναν νέο κώδικα.
Ο κώδικας θα πρέπει να μοιάζει με αυτό: Το όνομα της κλάσης μπορεί να είναι οτιδήποτε, αλλά είναι καλύτερο να έχετε ένα όνομα που δείχνει τι είδους κώδικας δημιουργείται αυτόματα.
SampleGenerator
Προς το παρόν, αφήστε το ως .
Initialize
Σε αυτήν τη μέθοδο, θα γράψετε τη διαδικασία ανάλυσης κώδικα και δημιουργίας κώδικα.
using Microsoft.CodeAnalysis;
namespace CodeGenerator
{
[Generator(LanguageNames.CSharp)]
public partial class SampleGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
}
}
}
Στη συνέχεια, δημιουργήστε ένα έργο για την εφαρμογή για την οποία θέλετε να δημιουργήσετε αυτόματα κώδικα. Μπορεί να είναι οτιδήποτε, αλλά εδώ θα το χρησιμοποιήσουμε απλώς ως εφαρμογή κονσόλας. Ο τύπος και η έκδοση του πλαισίου του έργου στο οποίο δημιουργείται αυτόματα ο κώδικας αντιστοιχεί στον γενικό αριθμό.
Προσθέστε μια αναφορά στο έργο γεννήτριας προέλευσης από το έργο από την πλευρά της εφαρμογής.
Ορισμένες ρυθμίσεις δεν μπορούν να οριστούν στις ιδιότητες, επομένως ανοίξτε το αρχείο έργου σε κώδικα.
Νομίζω ότι μοιάζει με αυτό:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CodeGenerator\CodeGenerator.csproj" />
</ItemGroup>
</Project>
Προσθέστε τις ρυθμίσεις για το αναφερόμενο έργο ως εξής: Βεβαιωθείτε ότι δεν κάνετε λάθος στη σύνταξη της XML.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CodeGenerator\CodeGenerator.csproj">
<OutputItemType>Analyzer</OutputItemType>
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
</Project>
Στη συνέχεια, ανοίξτε τις ιδιότητες του έργου στην πλευρά "Code Generator".
Κάντε κλικ στη σύνδεση για να ανοίξετε το περιβάλλον εργασίας χρήστη ιδιοτήτων εκκίνησης εντοπισμού σφαλμάτων.
Διαγράψτε το αρχικό προφίλ επειδή δεν θέλετε να το χρησιμοποιήσετε.
Προσθέστε ένα νέο προφίλ.
Επιλέξτε Roslyn Component.
Εάν έχετε κάνει τις ρυθμίσεις μέχρι στιγμής, θα πρέπει να μπορείτε να επιλέξετε το έργο εφαρμογής, οπότε επιλέξτε το.
Αυτό είναι το τέλος της προετοιμασίας.
Ελέγξτε αν μπορείτε να εντοπίσετε σφάλματα
Ανοίξτε τον πηγαίο κώδικα γεννήτριας και Initialize
τοποθετήστε ένα σημείο διακοπής στο τέλος της μεθόδου.
Ας εντοπίσουμε σφάλματα στη γεννήτρια πηγής.
Εάν η διαδικασία σταματήσει στο σημείο διακοπής, μπορείτε να επιβεβαιώσετε ότι εφαρμόζετε κανονικά εντοπισμό σφαλμάτων. Αυτό θα πρέπει να κάνει την ανάπτυξη της γεννήτριας πηγής σας αρκετά εύκολη.
Προς το παρόν, ας εξάγουμε έναν σταθερό κωδικό
Αρχικά, ας προσπαθήσουμε να εξάγουμε εύκολα έναν σταθερό κωδικό. Είναι εύκολο γιατί δεν χρειάζεται καν να αναλύσετε τον κώδικα. Ακόμα κι αν είναι ένας σταθερός κώδικας, αντιμετωπίζεται ως συμβολοσειρά, οπότε είναι δυνατό να αυξηθεί η παραγωγή κώδικα σε σταθερή μορφή με τη δημιουργία ενός προγράμματος.
Εάν θέλετε να εξάγετε έναν σταθερό κωδικό, μπορείτε context.RegisterPostInitializationOutput
να το κάνετε χρησιμοποιώντας το .
Ακολουθεί ένα παράδειγμα της εξόδου κώδικα.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace CodeGenerator;
[Generator(LanguageNames.CSharp)]
public partial class SampleGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// コードを解析せずそのままコードを出力するならこれを使用します
context.RegisterPostInitializationOutput(static postInitializationContext =>
{
// コード生成処理が中断されることもあるので CancellationToken 処理は入れたほうがいいです
System.Threading.CancellationToken token = postInitializationContext.CancellationToken;
token.ThrowIfCancellationRequested();
// 出力するコードを作ります
var source = """
internal static class SampleClass
{
public static void Hello() => Console.WriteLine("Hello Source Generator!!");
}
""";
// コードファイルに出力します (実際に目に見えるどこかのフォルダに出力されるわけではありません)
postInitializationContext.AddSource("SampleGeneratedFile.cs", SourceText.From(source, Encoding.UTF8));
});
}
}
Το περιεχόμενο του κώδικα είναι όπως γράφτηκε στα σχόλια, οπότε θα παραλείψω τις λεπτομέρειες. Δημιουργήστε το και βεβαιωθείτε ότι δεν υπάρχουν σφάλματα.
Όταν ολοκληρωθεί η κατασκευή, μπορείτε να επεκτείνετε τους "Αναλυτές" στο έργο εφαρμογής για να δείτε τον κώδικα που δημιουργείται από τη γεννήτρια κώδικα.
Εάν δεν το βλέπετε, επανεκκινήστε το Visual Studio και ελέγξτε το. Φαίνεται ότι αυτήν τη στιγμή, το Visual Studio ενδέχεται να μην ενημερωθεί, εκτός εάν το επανεκκινήσετε. Η ευφυΐα και η επισήμανση σύνταξης που χρησιμοποιούνται κατά τη δημιουργία προγραμμάτων είναι παρόμοιες. Ωστόσο, δεδομένου ότι ο ίδιος ο κώδικας αντικατοπτρίζεται κατά τη στιγμή της κατασκευής, φαίνεται ότι το πρόγραμμα αντικατοπτρίζεται από την πλευρά της εφαρμογής.
Μόλις δημιουργηθεί ο κώδικας, δοκιμάστε να τον χρησιμοποιήσετε στην εφαρμογή σας. Θα πρέπει να λειτουργεί καλά.
Ανάλυση και δημιουργία κώδικα
Εάν απλώς επεξεργάζεστε τον κώδικα κανονικά και τον εξάγετε, δεν διαφέρει πολύ από άλλη αυτόματη δημιουργία κώδικα και δεν μπορείτε να επωφεληθείτε από τα οφέλη της γεννήτριας πηγής. Τώρα λοιπόν ας αναλύσουμε τον κώδικα του έργου εφαρμογής και ας δημιουργήσουμε τον κώδικα ανάλογα.
Ωστόσο, η ανάλυση του κώδικα είναι αρκετά βαθιά, οπότε δεν μπορώ να εξηγήσω τα πάντα εδώ. Προς το παρόν, θα εξηγήσω μέχρι το σημείο όπου μπορείτε να αναλύσετε και να εξάγετε τον κώδικα. Αν θέλετε να πάτε βαθύτερα, κάντε τη δική σας έρευνα.
Προς το παρόν, για παράδειγμα, θα ήθελα να δημιουργήσω αυτόματα τον κώδικα "προσθέστε μια μέθοδο σε Reset
όλες τις δημιουργημένες και επαναφέρετε την τιμή default
όλων των ιδιοτήτων σε ".
Τα τελευταία χρόνια, φαίνεται ότι προτιμάται ο χειρισμός αμετάβλητων περιπτώσεων, αλλά στα προγράμματα παιχνιδιών, δεν είναι δυνατή η δημιουργία μιας νέας παρουσίας κάθε καρέ, οπότε νομίζω ότι υπάρχει μια χρήση για τη διαδικασία επαναφοράς της τιμής.
default
Μπορεί να θέλετε να ορίσετε κάτι άλλο από αυτό, αλλά αν το κάνετε αυτό, θα είναι ένα μακρύ πρώτο παράδειγμα, οπότε εφαρμόστε το μόνοι σας.
Πλευρά γεννήτριας κώδικα
Δημιουργήστε μια νέα κλάση με σκοπό τη διατήρηση της γεννήτριας που δημιουργήσατε πριν. Εάν αλλάξει το περιεχόμενο που θα δημιουργηθεί αυτόματα, είναι προτιμότερο να δημιουργήσετε ένα νέο σε άλλη τάξη.
Ο μεταγλωττιστής Roslyn φροντίζει για τη δομή του κώδικα, οπότε αυτό που πρόκειται να κάνουμε εδώ είναι να αναλύσουμε τα δομημένα δεδομένα και να γράψουμε τον κώδικα.
Πρώτον, θα δημοσιεύσω όλο τον κώδικα. Προσπάθησα να έχω όσο το δυνατόν λιγότερο κώδικα. Αυτή τη φορά λειτουργεί, αλλά είναι μόνο σε ένα επίπεδο που κινείται ως κύριος/κυρία, οπότε όταν προχωρήσετε πραγματικά στην ανάπτυξη, θα λείπουν αρκετά μέρη. Βελτιώστε εκεί όπως απαιτείται.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;
using System.Text;
using System.Threading;
namespace CodeGenerator;
[Generator(LanguageNames.CSharp)]
public partial class Sample2Generator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 構文を解析し処理対象のノードをコード出力用に変換します
var syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(
// すべてのノードが処理対象となるため、predicate で処理対象とするノードを限定します
predicate: static (node, cancelToken) =>
{
// 処理が中断される場面は多々あるのでいつでもキャンセルできるようにしておく
cancelToken.ThrowIfCancellationRequested();
// クラスのノードのみを対象とする (record とかつけると別な種類のノードになるので注意)
// また partial class であること
return node is ClassDeclarationSyntax nodeClass && nodeClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
},
// 対象のノードをコード出力に必要な形に変換します
transform: static (contextSyntax, cancelToken) =>
{
// いつでもキャンセルできるようにしておく
cancelToken.ThrowIfCancellationRequested();
// 今回は ClassDeclarationSyntax 確定なのでそのままキャストします
ClassDeclarationSyntax classDecl = (ClassDeclarationSyntax)contextSyntax.Node;
// データと構文から宣言されたシンボルを取得します
// 今回利用場面がほとんどないですが取得しておくといろんな情報を取得できるので便利です
var symbol = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(contextSyntax.SemanticModel, classDecl, cancelToken)!;
// 出力に必要な値を返します。大抵の型を渡すことができます
return (symbol, classDecl);
}
);
// 解析し変換した情報をもとにコードを出力します
context.RegisterSourceOutput(syntaxProvider, static (contextSource, input) =>
{
// いつでもキャンセルできるようにしておく
CancellationToken cancelToken = contextSource.CancellationToken;
cancelToken.ThrowIfCancellationRequested();
// 解析後に渡された値を受け取ります
var (symbol, classDecl) = input;
StringBuilder sbInitValues = new();
// プロパティを列挙して値を初期化するコードを生成
foreach (var syntax in classDecl.Members.OfType<PropertyDeclarationSyntax>())
{
sbInitValues.AppendLine($"{syntax.Identifier.ValueText} = default;");
}
// 出力するコードを作成
var source = @$"
{string.Join(" ", classDecl.Modifiers.Select(x => x.ToString()))} class {symbol.Name}
{{
public void Reset()
{{
{sbInitValues}
}}
}}";
// ファイル名はクラス名に合わせておきます。作成したコードを出力します
contextSource.AddSource($"{symbol.Name}.g.cs", source);
});
}
}
Έχω γράψει γι 'αυτό στα σχόλια, αλλά θα το εξηγήσω σε ορισμένα μέρη.
// 構文を解析し処理対象のノードをコード出力用に変換します
var syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(
// すべてのノードが処理対象となるため、predicate で処理対象とするノードを限定します
predicate: ...,
// 対象のノードをコード出力に必要な形に変換します
transform: ...
);
context.SyntaxProvider.CreateSyntaxProvider
για να αναλύσετε τον κώδικα στο έργο εφαρμογής και να τον δομήσετε όσο το δυνατόν περισσότερο.
Όλα χωρίζονται σε κόμβους και χρησιμοποιούμε για να καθορίσουμε ποιους από αυτούς θέλουμε να επεξεργαστούμε predicate
.
Επίσης, εάν είναι απαραίτητο transform
, μετατρέψτε το επεξεργασμένο αντικείμενο με και περάστε το στην πλευρά εξόδου.
predicate
Σε αυτήν την περίπτωση, κάνουμε τα εξής.
// すべてのノードが処理対象となるため、predicate で処理対象とするノードを限定します
predicate: static (node, cancelToken) =>
{
// 処理が中断される場面は多々あるのでいつでもキャンセルできるようにしておく
cancelToken.ThrowIfCancellationRequested();
// クラスのノードのみを対象とする (record とかつけると別な種類のノードになるので注意)
// また partial class であること
return node is ClassDeclarationSyntax nodeClass && nodeClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
},
Πρώτα απ 'όλα, όπως συμβαίνει με οποιαδήποτε διαδικασία, η διαδικασία αυτόματης δημιουργίας κώδικα μπορεί πάντα να ακυρωθεί πρόωρα.
Χρησιμοποιήστε λοιπόν το διακριτικό ακύρωσης για να ThrowIfCancellationRequested
καλέσετε, ώστε να μπορείτε να διακόψετε ανά πάσα στιγμή.
predicate
Τώρα που καλείται κάθε κόμβος, θέλουμε να καθορίσουμε ποιος είναι αυτός που θέλουμε να επεξεργαστούμε.
Δεδομένου ότι υπάρχει ένας τεράστιος αριθμός από αυτούς, είναι καλύτερο να τα περιορίσετε εδώ σε κάποιο βαθμό.
Δεδομένου ότι πρόκειται να προσθέσουμε επεξεργασία στην τάξη αυτή τη φορά, θα καθορίσουμε ClassDeclarationSyntax
αν είναι και θα καθορίσουμε αν θα γίνει επεξεργασία μόνο της κλάσης.
partial class
Επίσης, δεδομένου ότι ο κώδικας επισυνάπτεται με , partial class
τίθεται ως κρίση.
// 対象のノードをコード出力に必要な形に変換します
transform: static (contextSyntax, cancelToken) =>
{
// いつでもキャンセルできるようにしておく
cancelToken.ThrowIfCancellationRequested();
// 今回は ClassDeclarationSyntax 確定なのでそのままキャストします
ClassDeclarationSyntax classDecl = (ClassDeclarationSyntax)contextSyntax.Node;
// データと構文から宣言されたシンボルを取得します
// 今回利用場面がほとんどないですが取得しておくといろんな情報を取得できるので便利です
var symbol = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(contextSyntax.SemanticModel, classDecl, cancelToken)!;
// 出力に必要な値を返します。大抵の型を渡すことができます
return (symbol, classDecl);
}
transform
Σας επιτρέπει να μετατρέψετε την ανάλυση στην απαιτούμενη φόρμα και να την περάσετε στην έξοδο κώδικα.
Αυτή τη φορά, δεν κάνουμε πολλές μετατροπές, αλλά περνάμε την τιμή στην πλευρά εξόδου όπως είναι return
.
Παίρνουμε "δηλωμένα σύμβολα" στην πορεία, αλλάSemanticModel
μπορούμε να πάρουμε πολλές πρόσθετες πληροφορίες χρησιμοποιώντας και Syntac
, οπότε νομίζω ότι μπορούμε να τις πάρουμε αν χρειαστεί.
return
Οι πλειάδες δημιουργούνται και επιστρέφονται, αλλά η διαμόρφωση των δεδομένων που θα περάσουν στην πλευρά εξόδου μπορεί να είναι οτιδήποτε.
Εάν θέλετε να μεταβιβάσετε πολλά δεδομένα, μπορείτε να δημιουργήσετε μια πλειάδα όπως αυτή ή μπορείτε να ορίσετε τη δική σας κλάση και να την μεταβιβάσετε.
// 解析し変換した情報をもとにコードを出力します
context.RegisterSourceOutput(syntaxProvider, static (contextSource, input) =>
{
// いつでもキャンセルできるようにしておく
CancellationToken cancelToken = contextSource.CancellationToken;
cancelToken.ThrowIfCancellationRequested();
// 解析後に渡された値を受け取ります
var (symbol, classDecl) = input;
StringBuilder sbInitValues = new();
// プロパティを列挙して値を初期化するコードを生成
foreach (var syntax in classDecl.Members.OfType<PropertyDeclarationSyntax>())
{
sbInitValues.AppendLine($"{syntax.Identifier.ValueText} = default;");
}
// 出力するコードを作成
var source = @$"
{string.Join(" ", classDecl.Modifiers.Select(x => x.ToString()))} class {symbol.Name}
{{
public void Reset()
{{
{sbInitValues}
}}
}}";
// ファイル名はクラス名に合わせておきます。作成したコードを出力します
contextSource.AddSource($"{symbol.Name}.g.cs", source);
});
context.RegisterSourceOutput
context.SyntaxProvider.CreateSyntaxProvider
Τώρα, θα δημιουργήσουμε και θα εξάγουμε κώδικα με βάση τα δεδομένα που θα υποβληθούν σε επεξεργασία από το .
context.SyntaxProvider.CreateSyntaxProvider
return
Η τιμή του μπορεί να ληφθεί ως το δεύτερο όρισμα του Action
, επομένως ο κώδικας δημιουργείται με βάση αυτήν την τιμή.
Σε αυτήν την περίπτωση, δημιουργούμε κώδικα για να λάβουμε τη λίστα σύνταξης ιδιοτήτων από τη σύνταξη της κλάσης και να ορίσουμε την default
ιδιότητα σε από κάθε όνομα.
Μετά από αυτό, δημιουργήστε μια μέθοδο με βάση Reset
το όνομα της κλάσης, ενσωματώστε τον κώδικα της λίστας ιδιοτήτων που δημιουργήθηκε νωρίτερα και ορίστε ξανά Reset
την τιμή default
όλων των ιδιοτήτων σε Η μέθοδος ολοκληρώθηκε.
Ο κώδικας εξάγεται contextSource.AddSource
ξεχωριστά για κάθε κλάση στη μέθοδο.
Παρεμπιπτόντως, ο λόγος για τον οποίο έβαλα "g" στο όνομα αρχείου είναι να διευκολύνω τον προσδιορισμό του εάν δημιουργείται χειροκίνητα κώδικας ή δημιουργείται αυτόματα σφάλμα κώδικα όταν υπάρχει σφάλμα κατασκευής.
Εάν το κάνετε πραγματικά, θα λάβετε αιτήματα όπως "Θέλω να επαναφέρω το πεδίο", "Θέλω να το αρχικοποιήσω εκτός από προεπιλογή", "Θέλω να επαναφέρω μόνο τις αυτόματες ιδιότητες". Εάν τα βάλετε, το καλώδιο θα είναι μακρύ, οπότε προσπαθήστε να το κάνετε μόνοι σας.
Πλευρά έργου εφαρμογής
Αυτή τη φορά, δημιούργησα έναν κώδικα που επεκτείνει τη μέθοδο της τάξης, οπότε θα δημιουργήσω μια τάξη κατάλληλα.
Το περιεχόμενο έχει ως εξής, αλλά εάν υπάρχουν ιδιότητες, μπορείτε να χρησιμοποιήσετε τα υπόλοιπα ανάλογα με την περίπτωση.
Player.cs
public partial class Player
{
public string Name { get; set; } = "";
public float PositionX { get; set; }
public float PositionY { get; set; }
public int Level { get; set; } = 1;
public override string ToString()
{
return $"{nameof(Player)} {{{nameof(Name)} = {Name}, {nameof(PositionX)} = {PositionX}, {nameof(PositionY)} = {PositionY}, {nameof(Level)} = {Level}}}";
}
}
Item.cs
public partial class Item
{
public string Name { get; set; } = "";
public float PositionX { get; set; }
public float PositionY { get; set; }
public int Type { get; set; }
public override string ToString()
{
return $"{nameof(Item)} {{{nameof(Name)} = {Name}, {nameof(PositionX)} = {PositionX}, {nameof(PositionY)} = {PositionY}, {nameof(Type)} = {Type}}}";
}
}
Η μέθοδος επεκτείνεται όταν Reset
δημιουργείτε τον κώδικα, αλλά ενδέχεται να μην αντικατοπτρίζεται στο Visual Studio, επομένως επανεκκινήστε το Visual Studio εκείνη τη στιγμή.
Νομίζω ότι μπορείτε να δείτε ότι ο κώδικας επεκτείνεται αυτόματα στον αναλυτή.
Η εσοχή είναι περίεργη, αλλά μπορείτε να την αγνοήσετε επειδή βασικά δεν αγγίζετε τον κώδικα που δημιουργείται αυτόματα.
Program.cs
Δοκιμάστε να γράψετε κώδικα για να δείτε Reset
αν μπορείτε να καλέσετε τη μέθοδο.
Νομίζω ότι τα αποτελέσματα της εκτέλεσης αντικατοπτρίζονται όπως αναμενόταν.
Program.cs
Console.WriteLine("Hello, World!");
SampleClass.Hello();
Console.WriteLine();
Player player = new()
{
Name = "Mike",
PositionX = 10,
PositionY = 5.5f,
Level = 3,
};
Console.WriteLine(player);
player.Reset();
Console.WriteLine(player);
Console.WriteLine();
Item item = new()
{
Name = "Banana",
PositionX = 50,
PositionY = 53.5f,
Type = 12,
};
Console.WriteLine(item);
item.Reset();
Console.WriteLine(item);
Console.WriteLine();