国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

源生成器:根據(jù)需要自動(dòng)生成機(jī)械重復(fù)代碼

這篇具有很好參考價(jià)值的文章主要介紹了源生成器:根據(jù)需要自動(dòng)生成機(jī)械重復(fù)代碼。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

title: 源生成器:根據(jù)需要自動(dòng)生成機(jī)械重復(fù)代碼
date: 2022-02-02
tags:
- C#
- .NET
- Roslyn

前言

本文概述了利用.NET Compiler Platform(“Roslyn”)SDK 附帶的源生成器(Source Generator)自動(dòng)生成機(jī)械重復(fù)的代碼。關(guān)于這部分的基礎(chǔ)入門(mén)知識(shí)可以在MSDN[1]學(xué)到。

本文默認(rèn)已經(jīng)有一個(gè)解決方案,包含兩個(gè)項(xiàng)目。一個(gè)是普通C#項(xiàng)目,依賴于另一個(gè)源生成器項(xiàng)目。

創(chuàng)建及使用Attribute

此處以DependencyPropertyAttribute為例,可以為擁有本Attribute的類,自動(dòng)獲取所有定義過(guò)的屬性,并將它們?cè)谝粋€(gè)構(gòu)造函數(shù)里初始化。

DependencyProperty的名稱、類型、屬性改變處理函數(shù)都是必須指定的,可選指定內(nèi)容是屬性setter的公共性、該類型的null性、和默認(rèn)值??蛇x內(nèi)容有默認(rèn)值。

以下是DependencyPropertyAttribute的實(shí)現(xiàn):

using System;

namespace Attributes;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class DependencyPropertyAttribute<T> : Attribute where T : notnull
{
    public DependencyPropertyAttribute(string name, string propertyChanged = "")
    {
        Name = name;
        PropertyChanged = propertyChanged;
    }

    public string Name { get; }

    public string PropertyChanged { get; }

    public bool IsSetterPublic { get; init; } = true;

    public bool IsNullable { get; init; } = true;

    public string DefaultValue { get; init; } = "DependencyProperty.UnsetValue";
}

在.NET 7中,加入了新的泛型特性(Generic Attributes[2]),所以此處我們直接使用泛型。

以下是使用示例:

namespace Controls.IconButton;

[DependencyProperty<string>("Text", nameof(OnTextChanged))]
[DependencyProperty<IconElement>("Icon", nameof(OnIconChanged))]
public partial class IconButton : Button
{
    ...
}

這將會(huì)生成如下代碼:

using Microsoft.UI.Xaml;
using System;
using Microsoft.UI.Xaml.Controls;

#nullable enable
namespace Controls.IconButton
{
    partial class IconButton
    {
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(IconButton), new PropertyMetadata(DependencyProperty.UnsetValue, OnTextChanged));
        public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }

        public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(IconElement), typeof(IconButton), new PropertyMetadata(DependencyProperty.UnsetValue, OnIconChanged));
        public IconElement Icon { get => (IconElement)GetValue(IconProperty); set => SetValue(IconProperty, value); }
    }
}

注:DependencyPropertyAttribute中建議只使用基本類型的常量,因?yàn)閺?fù)雜類型不方便獲取。

注:被添加Attribute的類(如IconButton)要加partial關(guān)鍵字,否則會(huì)出重定義錯(cuò)誤。

注:DependencyPropertyAttribute中,只會(huì)用到構(gòu)造函數(shù)和可選指定內(nèi)容的屬性,這說(shuō)明實(shí)現(xiàn)可以簡(jiǎn)化為:

using System;

namespace Attributes;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class DependencyPropertyAttribute<T> : Attribute where T : notnull
{
    public DependencyPropertyAttribute(string name, string propertyChanged = "") { }

    public bool IsSetterPublic { get; init; }

    public bool IsNullable { get; init; }

    public string DefaultValue { get; init; }
}

因?yàn)楫?dāng)源生成器分析的時(shí)候,分析的是被捕獲的類(如IconButton)及其上下文,而非DependencyPropertyAttribute的,所以其他內(nèi)容實(shí)際上用不上。

但原來(lái)的寫(xiě)法方便將來(lái)可能需要反射本Attribute的操作,也方便閱讀,所以建議保留。

創(chuàng)建通用基類

TypeWithAttributeGenerator可以作為所有分析類型上的Attribute的分析器的模板基類。繼承它后只需傳入AttributeName便可以自動(dòng)執(zhí)行對(duì)應(yīng)方法了。

除了屬性AttributeName外,還有一個(gè)需要子類實(shí)現(xiàn)的是方法TypeWithAttribute。它傳入的參數(shù)分別是Attribute所在的類型和它所擁有的所有指定Attribute,可能有多個(gè)所以是數(shù)組。這個(gè)方法返回的就是生成的文件代碼,以string傳回;如果中途發(fā)生任何錯(cuò)誤無(wú)法生成,則返回null即可。

此處我們使用的是IIncrementalGenerator增量生成器。舊的源生成器在每次代碼有更改時(shí)都會(huì)掃描整個(gè)語(yǔ)法樹(shù),開(kāi)銷很大,新的增量生成器[3]通過(guò)管道[4]等方式遴選需要掃描的代碼,大大減少生成開(kāi)銷。增量生成器是Roslyn 4.0的新功能,對(duì)應(yīng)VS17.0(即Visual Studio?2022),也就是說(shuō)只有VS2022及以上的版本才可以使用。

using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using static SourceGenerator.Utilities.SourceGeneratorHelper;

namespace SourceGenerator;

public abstract class TypeWithAttributeGenerator : IIncrementalGenerator
{
    internal abstract string AttributeName { get; }

    // 注:由于我寫(xiě)的所有`Attribute`都是用的同一個(gè)命名空間,
    // 所以可以通過(guò)組合`AttributeNamespace`和`AttributeName`便可以得到完整名稱。
    // `AttributeNamespace`為"Attributes."
    private string AttributeFullName => AttributeNamespace + AttributeName;

    internal abstract string? TypeWithAttribute(INamedTypeSymbol typeSymbol, ImmutableArray<AttributeData> attributeList);

    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var generatorAttributes = context.SyntaxProvider.ForAttributeWithMetadataName(
            AttributeFullName,
            (_, _) => true,
            (syntaxContext, _) => syntaxContext
        ).Combine(context.CompilationProvider);

        context.RegisterSourceOutput(generatorAttributes, (spc, tuple) =>
        {
            var (ga, compilation) = tuple;

            // 注:此處我指定了一個(gè)特殊的`Attribute`,如果使用了它就禁用所有源生成器。
            // 如:[assembly: DisableSourceGenerator]
            if (compilation.Assembly.GetAttributes().Any(attrData => attrData.AttributeClass?.ToDisplayString() == DisableSourceGeneratorAttribute))
                return;

            if (ga.TargetSymbol is not INamedTypeSymbol symbol)
                return;

            if (TypeWithAttribute(symbol, ga.Attributes) is { } source)
                spc.AddSource(
                    // 不能重名
                    $"{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted))}_{AttributeFullName}.g.cs",
                    source);
        });
    }
}

獲取特性的重要方法

ForAttributeWithMetadataName<T>[5]是Roslyn 4.3.0新提供的API,這個(gè)方法可以根據(jù)所給的名字,找到所有擁有該Attribute的單元,用它寫(xiě)的代碼比之前簡(jiǎn)潔太多了,現(xiàn)在介紹一下這個(gè)方法:

它的第一個(gè)參數(shù)是:

string fullyQualifiedMetadataName

輸入Attribute的元數(shù)據(jù)全名即可,如果是泛型則應(yīng)該寫(xiě)為類似這樣的形式:

"Attributes.DependencyPropertyAttribute`1"

第二個(gè)參數(shù)是一個(gè)委托:

Func<Microsoft.CodeAnalysis.SyntaxNode, System.Threading.CancellationToken, bool> predicate

提供對(duì)應(yīng)class、property等擁有指定Attribute的單元(以下簡(jiǎn)稱“目標(biāo)單元”)的語(yǔ)法節(jié)點(diǎn)和取消標(biāo)識(shí),返回一個(gè)bool表示是否保留這項(xiàng),一般直接返回true即可。

第三個(gè)參數(shù)也是委托:

Func<Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext, System.Threading.CancellationToken, T> transform

提供目標(biāo)單元的一個(gè)“生成器特性語(yǔ)法上下文(GeneratorAttributeSyntaxContext)”和取消標(biāo)識(shí),返回你想保留的、關(guān)于這個(gè)單元的數(shù)據(jù),一般直接返回GeneratorAttributeSyntaxContext參數(shù)即可。

這個(gè)GeneratorAttributeSyntaxContext十分好用,他有四個(gè)屬性,都是我們需要的:

第一個(gè)是目標(biāo)節(jié)點(diǎn),即目標(biāo)單元的語(yǔ)法樹(shù),一般是TypeDeclarationSyntax的子類

SyntaxNode TargetNode

第二個(gè)是目標(biāo)符號(hào),一般是INamedTypeSymbol或IPropertySymbol等

ISymbol TargetSymbol

第三個(gè)是語(yǔ)義模型,即目標(biāo)單元所在文件的語(yǔ)法樹(shù)

SemanticModel SemanticModel

第四個(gè)是特性數(shù)組,是目標(biāo)單元上所有的指定Attribute

ImmutableArray<AttributeData> Attributes

原來(lái)這些數(shù)據(jù)都需要我們?cè)贓xecute中自己收集,而現(xiàn)在微軟已經(jīng)全部封裝好了。

實(shí)現(xiàn)生成器

接下來(lái)我們通過(guò)繼承來(lái)實(shí)現(xiàn)生成器:

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;

namespace SourceGenerator;

[Generator]
public class DependencyPropertyGenerator : TypeWithAttributeGenerator
{
    internal override string AttributeName => "DependencyPropertyAttribute`1";

    internal override string? TypeWithAttribute(INamedTypeSymbol typeSymbol, ImmutableArray<AttributeData> attributeList)
    {
        ...
    }
}

我們主要說(shuō)一下如何獲取類型上的Attribute。如:

[DependencyProperty<string>("Name", nameof(Method), IsNullable = true)]

這種寫(xiě)法其實(shí)是一個(gè)構(gòu)造函數(shù),只是不像普通的類型那樣用new而已。所以獲取DependencyPropertyAttribute的參數(shù)只需要分析他的構(gòu)造函數(shù)即可:

internal override string? TypeWithAttribute(INamedTypeSymbol typeSymbol, ImmutableArray<AttributeData> attributeList)
{
    foreach (var attribute in attributeList)
    {
        if (attribute.AttributeClass is not { TypeArguments: [var type, ..] })
            return null;

        if (attribute.ConstructorArguments is not
            [
                { Value: string propertyName },
                { Value: string defaultValue },
                { Value: string propertyChanged },
                ..
            ])
            continue;

        var isSetterPrivate = false;
        var isNullable = false;

        foreach (var namedArgument in attribute.NamedArguments)
            if (namedArgument.Value.Value is { } value)
                switch (namedArgument.Key)
                {
                    case "IsSetterPrivate":
                        isSetterPrivate = (bool)value;
                        break;
                    case "IsNullable":
                        isNullable = (bool)value;
                        break;
                }
        
        ...
    }
}

這便是分析一個(gè)構(gòu)造函數(shù)的代碼了,還比較簡(jiǎn)短吧?
這塊代碼其實(shí)主要分為三個(gè)部分,我們可以以這句為例分析一下:

[DependencyProperty<string>("Name", nameof(Method), IsNullable = true)]

第一部分:這塊是獲取泛型參數(shù),即<string>。如果沒(méi)有泛型參數(shù)肯定是錯(cuò)誤的,所以直接返回空值。

if (attribute.AttributeClass is not { TypeArguments: [var type, ..] })
    return null;

第二部分:這塊是獲取構(gòu)造函數(shù)的參數(shù),即"Name", nameof(Method)部分。注意如果就算使用了缺省參數(shù)的話,它的值也是可以在這里捕捉到的。如果有多個(gè)構(gòu)造函數(shù)的話簡(jiǎn)單替換為switch語(yǔ)句即可。

if (attribute.ConstructorArguments is not
    [
        { Value: string propertyName },
        { Value: string defaultValue },
        { Value: string propertyChanged },
        ..
    ])
    continue;

第三部分:這塊是獲取初始化列表,即IsNullable = true。這里的賦值是在執(zhí)行完構(gòu)造函數(shù)之后才會(huì)發(fā)生,所以嚴(yán)格來(lái)說(shuō)其實(shí)不是構(gòu)造函數(shù)的一部分,但我們確實(shí)可以獲得執(zhí)行參數(shù)。注意這里和上面不一樣,如果沒(méi)有指定這些參數(shù)的話,這里就捕捉不到,所以我們不能獲取不到就返回空值了,而要直接給參數(shù)賦值為默認(rèn)值。

var isSetterPrivate = false;
var isNullable = false;

foreach (var namedArgument in attribute.NamedArguments)
    if (namedArgument.Value.Value is { } value)
        switch (namedArgument.Key)
        {
            case "IsSetterPrivate":
                isSetterPrivate = (bool)value;
                break;
            case "IsNullable":
                isNullable = (bool)value;
                break;
        }

以上是分析構(gòu)造函數(shù)的部分,接下來(lái)就是絕大部分程序員的老本行:折騰字符串了。根據(jù)Attribute輸入和程序原本的邏輯拼接字符串,最后將拼接成的字符串源碼返回,即可成功運(yùn)行了!折騰字符串的部分就不仔細(xì)介紹了,大家有興趣可以看我的倉(cāng)庫(kù)[6]


  1. Source Generators ??

  2. Generic Attributes ??

  3. GitHub-IncrementalGenerators ??

  4. Creating a source generator ??

  5. SyntaxValueProvider.ForAttributeWithMetadataName Method ??

  6. WinUI3Utilities ??文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-788592.html

到了這里,關(guān)于源生成器:根據(jù)需要自動(dòng)生成機(jī)械重復(fù)代碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Javascript——生成器(Generator)自動(dòng)執(zhí)行

    Generator自動(dòng)化是通過(guò)Thunk函數(shù)進(jìn)行實(shí)現(xiàn),寫(xiě)這篇文章的目的是為了理解通過(guò)Thunk實(shí)現(xiàn)Generator函數(shù)的自動(dòng)執(zhí)行。 我們可以帶入一個(gè)業(yè)務(wù)場(chǎng)景來(lái)幫助我們理解Thunk實(shí)現(xiàn)Generator自動(dòng)執(zhí)行的好處,業(yè)務(wù)場(chǎng)景如下: 假設(shè)小明今天干了一件事情是: ? ? ? ? 1、買(mǎi)菜 ? ? ? ? 2、買(mǎi)完菜回家

    2024年03月25日
    瀏覽(30)
  • vscode 插件 codegeex 自動(dòng)代碼生成器

    vscode 插件 codegeex 自動(dòng)代碼生成器

    CodeGeeX是一個(gè)具有130億參數(shù)的多編程語(yǔ)言代碼生成預(yù)訓(xùn)練模型,使用超過(guò)二十種編程語(yǔ)言訓(xùn)練得到。 基于CodeGeeX開(kāi)發(fā)的插件可以實(shí)現(xiàn)通過(guò)描述生成代碼、補(bǔ)全代碼、代碼翻譯等一系列功能。 CodeGeeX同樣提供可以定制的提示模式(Prompt Mode),構(gòu)建專屬的編程助手 自動(dòng)代碼生成

    2024年02月09日
    瀏覽(31)
  • AI自動(dòng)寫(xiě)文章_免費(fèi)在線文章生成器

    AI自動(dòng)寫(xiě)文章_免費(fèi)在線文章生成器

    自動(dòng)寫(xiě)文章生成器是一種利用人工智能和自然語(yǔ)言處理技術(shù),幫助用戶快速生成文章的工具。該軟件可以根據(jù)用戶的需求和選擇,自動(dòng)生成符合要求的文章,無(wú)需手動(dòng)編寫(xiě)和修改。 自動(dòng)寫(xiě)文章生成器的主要功能包括以下幾個(gè)方面: 選擇:用戶可選擇相關(guān)的主題或關(guān)鍵

    2024年02月03日
    瀏覽(24)
  • 使用Puppeteer構(gòu)建博客內(nèi)容的自動(dòng)標(biāo)簽生成器

    使用Puppeteer構(gòu)建博客內(nèi)容的自動(dòng)標(biāo)簽生成器

    標(biāo)簽是一種用于描述和分類博客內(nèi)容的元數(shù)據(jù),它可以幫助讀者快速找到感興趣的主題,也可以提高博客的搜索引擎優(yōu)化(SEO)。然而,手動(dòng)為每篇博客文章添加合適的標(biāo)簽是一件費(fèi)時(shí)費(fèi)力的工作,有時(shí)候也容易遺漏或重復(fù)。本文將介紹如何使用Puppeteer這個(gè)強(qiáng)大的Node.js庫(kù)來(lái)構(gòu)

    2024年02月10日
    瀏覽(25)
  • python游戲編程代碼大全,python代碼自動(dòng)生成器

    python游戲編程代碼大全,python代碼自動(dòng)生成器

    大家好,給大家分享一下python游戲編程入門(mén)游戲代碼,很多人還不知道這一點(diǎn)。下面詳細(xì)解釋一下?,F(xiàn)在讓我們來(lái)看看! 大家好,小編來(lái)為大家解答以下問(wèn)題,python游戲代碼大全可復(fù)制100行,python游戲代碼大全可復(fù)制免費(fèi),今天讓我們一起來(lái)看看吧! python有趣的編程代碼

    2024年02月21日
    瀏覽(22)
  • 揭秘藝術(shù)的未來(lái):AI繪畫(huà)自動(dòng)生成器的魔法

    A. 介紹AI在藝術(shù)創(chuàng)作中的興起 隨著人工智能技術(shù)的迅猛發(fā)展,它已經(jīng)逐漸滲透到了各個(gè)領(lǐng)域,其中包括藝術(shù)創(chuàng)作。傳統(tǒng)上,藝術(shù)創(chuàng)作一直被認(rèn)為是人類獨(dú)有的領(lǐng)域,需要藝術(shù)家具備獨(dú)特的創(chuàng)造力和技能。然而,隨著AI技術(shù)的崛起,人們開(kāi)始探索將機(jī)器學(xué)習(xí)和深度學(xué)習(xí)應(yīng)用于藝

    2024年04月14日
    瀏覽(31)
  • 最強(qiáng)自動(dòng)化測(cè)試框架Playwright(21)-測(cè)試生成器inspector

    最強(qiáng)自動(dòng)化測(cè)試框架Playwright(21)-測(cè)試生成器inspector

    運(yùn)行該命令時(shí),將打開(kāi)兩個(gè)窗口,一個(gè)瀏覽器窗口,可以在其中與要測(cè)試的網(wǎng)站進(jìn)行交互,另一個(gè)是Playwright Inspector窗口,可以在其中記錄測(cè)試,然后將其復(fù)制到編輯器中。 使用該命令運(yùn)行測(cè)試生成器,后面為要為其生成測(cè)試的網(wǎng)站的 URL。URL 是可選的 運(yùn)行命令并在瀏覽器窗

    2024年02月13日
    瀏覽(55)
  • springboot引入sqlite3,mybaits自動(dòng)生成器連接sqlite

    1, pom文件里添加依賴 2,yml文件引入sqlite數(shù)據(jù)庫(kù) 3,在generator.properties引入sqlite 4,運(yùn)行Generator即可生成

    2024年01月23日
    瀏覽(22)
  • 【重要】springboot實(shí)戰(zhàn)(六)之mybatis-plus代碼自動(dòng)生成器

    【重要】springboot實(shí)戰(zhàn)(六)之mybatis-plus代碼自動(dòng)生成器

    目錄 環(huán)境: 步驟: 1.添加依賴 2.配置代碼 3.運(yùn)行 測(cè)試 1.測(cè)試生成的service 1.1、service用法 2.分頁(yè)查詢 2.1、分頁(yè)插件配置? 2.2、測(cè)試 3.源碼 jdk:1.8 springboot版本:2.7.15 mybatis-plus版本:3.5.1以上 (本文章用的當(dāng)前最新版本:3.5.3.2,代碼適用于3.5.1版本以上的版本) 在測(cè)試類中創(chuàng)建

    2024年02月03日
    瀏覽(21)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包