Share via


Oktatóanyag: Egyéni sztring interpolációkezelő írása

Az oktatóanyag segítségével megtanulhatja a következőket:

  • A sztring interpolációs kezelőjének mintájának implementálása
  • Egy sztring interpolációs művelet során lépjen kapcsolatba a vevővel.
  • Argumentumok hozzáadása a sztring interpolációkezelőhöz
  • A sztringinterpoláció új kódtár-funkcióinak ismertetése

Előfeltételek

Be kell állítania a gépet a .NET 6 futtatására, beleértve a C# 10 fordítót is. A C# 10 fordító a Visual Studio 2022-től vagy a .NET 6 SDK-tól kezdve érhető el.

Ez az oktatóanyag feltételezi, hogy ismeri a C# és a .NET használatát, beleértve a Visual Studiót vagy a .NET CLI-t is.

Új körvonal

A C# 10 támogatja az egyéni interpolált sztringkezelőt. Az interpolált sztringkezelő olyan típus, amely egy interpolált sztringben dolgozza fel a helyőrző kifejezést. Egyéni kezelő nélkül a helyőrzők feldolgozása a következőhöz String.Formathasonlóan történik. Minden helyőrző szövegként van formázva, majd az összetevők összefűzve alkotják az eredményül kapott sztringet.

Bármely olyan forgatókönyvhöz írhat kezelőt, amelyben az eredményül kapott sztringre vonatkozó információkat használja. Használni fogja? Milyen korlátozások vannak a formátumon? Néhány példa:

  • Előfordulhat, hogy az eredményül kapott sztringek egyike sem haladja meg a korlátot, például 80 karaktert. Az interpolált sztringek feldolgozhatók rögzített hosszúságú puffer kitöltéséhez, és a puffer hosszának elérése után leállíthatja a feldolgozást.
  • Lehet, hogy táblázatos formátumú, és minden helyőrzőnek rögzített hosszúságúnak kell lennie. Az egyéni kezelők ezt kényszeríthetik ahelyett, hogy az összes ügyfélkódot a megfelelőségre kényszeríteni.

Ebben az oktatóanyagban egy sztring interpolációs kezelőt fog létrehozni az egyik fő teljesítményforgatókönyvhez: naplózási kódtárakhoz. A konfigurált naplószinttől függően nincs szükség naplóüzenet létrehozására. Ha a naplózás ki van kapcsolva, nincs szükség egy sztring interpolált sztringkifejezésből való létrehozására. Az üzenet soha nem lesz kinyomtatva, így a sztringösszefűzés kihagyható. Emellett a helyőrzőkben használt kifejezéseket, beleértve a verem nyomkövetését is, nem kell elvégezni.

Egy interpolált sztringkezelő meg tudja állapítani, hogy a formázott sztring lesz-e használva, és csak akkor végezze el a szükséges munkát, ha szükséges.

Kezdeti megvalósítás

Kezdjük egy alapszintű Logger osztálysal, amely különböző szinteket támogat:

public enum LogLevel
{
    Off,
    Critical,
    Error,
    Warning,
    Information,
    Trace
}

public class Logger
{
    public LogLevel EnabledLevel { get; init; } = LogLevel.Error;

    public void LogMessage(LogLevel level, string msg)
    {
        if (EnabledLevel < level) return;
        Console.WriteLine(msg);
    }
}

Ez Logger hat különböző szintet támogat. Ha egy üzenet nem adja át a naplószintű szűrőt, nincs kimenet. A naplózó nyilvános API-ja egy (teljesen formázott) sztringet fogad el üzenetként. A sztring létrehozására vonatkozó összes munka már befejeződött.

A kezelőminta implementálása

Ez a lépés egy interpolált sztringkezelő létrehozása, amely újra létrehozza az aktuális viselkedést. Az interpolált sztringkezelő olyan típus, amely a következő jellemzőkkel rendelkezik:

  • A System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute típusra alkalmazott.
  • Két paraméterrel rendelkező int konstruktor és literalLengthformattedCount. (További paraméterek engedélyezettek).
  • Nyilvános AppendLiteral módszer az aláírással: public void AppendLiteral(string s).
  • Általános nyilvános AppendFormatted módszer az aláírással: public void AppendFormatted<T>(T t).

A szerkesztő belsőleg létrehozza a formázott sztringet, és egy tagot biztosít az ügyfélnek a sztring lekéréséhez. Az alábbi kód az alábbi követelményeknek megfelelő típust LogInterpolatedStringHandler mutatja be:

[InterpolatedStringHandler]
public ref struct LogInterpolatedStringHandler
{
    // Storage for the built-up string
    StringBuilder builder;

    public LogInterpolatedStringHandler(int literalLength, int formattedCount)
    {
        builder = new StringBuilder(literalLength);
        Console.WriteLine($"\tliteral length: {literalLength}, formattedCount: {formattedCount}");
    }

    public void AppendLiteral(string s)
    {
        Console.WriteLine($"\tAppendLiteral called: {{{s}}}");
        
        builder.Append(s);
        Console.WriteLine($"\tAppended the literal string");
    }

    public void AppendFormatted<T>(T t)
    {
        Console.WriteLine($"\tAppendFormatted called: {{{t}}} is of type {typeof(T)}");

        builder.Append(t?.ToString());
        Console.WriteLine($"\tAppended the formatted object");
    }

    internal string GetFormattedText() => builder.ToString();
}

Most már hozzáadhat túlterhelést LogMessage az osztályhoz az Logger új interpolált sztringkezelő kipróbálásához:

public void LogMessage(LogLevel level, LogInterpolatedStringHandler builder)
{
    if (EnabledLevel < level) return;
    Console.WriteLine(builder.GetFormattedText());
}

Nem kell eltávolítania az eredeti LogMessage metódust, a fordító az interpolált kezelőparaméterrel rendelkező metódust részesíti előnyben egy string paraméterrel rendelkező metódussal szemben, ha az argumentum interpolált sztringkifejezés.

Az új kezelő meghívását a következő kód használatával ellenőrizheti fő programként:

var logger = new Logger() { EnabledLevel = LogLevel.Warning };
var time = DateTime.Now;

logger.LogMessage(LogLevel.Error, $"Error Level. CurrentTime: {time}. This is an error. It will be printed.");
logger.LogMessage(LogLevel.Trace, $"Trace Level. CurrentTime: {time}. This won't be printed.");
logger.LogMessage(LogLevel.Warning, "Warning Level. This warning is a string, not an interpolated string expression.");

Az alkalmazás futtatása az alábbi szöveghez hasonló kimenetet hoz létre:

        literal length: 65, formattedCount: 1
        AppendLiteral called: {Error Level. CurrentTime: }
        Appended the literal string
        AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
        Appended the formatted object
        AppendLiteral called: {. This is an error. It will be printed.}
        Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:19:10 PM. This is an error. It will be printed.
        literal length: 50, formattedCount: 1
        AppendLiteral called: {Trace Level. CurrentTime: }
        Appended the literal string
        AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
        Appended the formatted object
        AppendLiteral called: {. This won't be printed.}
        Appended the literal string
Warning Level. This warning is a string, not an interpolated string expression.

A kimenet nyomon követése során láthatja, hogy a fordító hogyan ad hozzá kódot a kezelő meghívásához és a sztring létrehozásához:

  • A fordító egy hívást ad hozzá a kezelő létrehozásához, átadva a formátumsztringben szereplő literális szöveg teljes hosszát és a helyőrzők számát.
  • A fordító hívásokat ad hozzá a AppendLiteralAppendFormatted literális sztring minden szakaszához és minden helyőrzőhöz.
  • A fordító argumentumként meghívja a LogMessageCoreInterpolatedStringHandler metódust.

Végül figyelje meg, hogy az utolsó figyelmeztetés nem hívja meg az interpolált sztringkezelőt. Az argumentum egy string, így a hívás sztringparaméterrel hívja meg a másik túlterhelést.

További képességek hozzáadása a kezelőhöz

Az interpolált sztringkezelő előző verziója implementálja a mintát. Az összes helyőrző kifejezés feldolgozásának elkerülése érdekében további információkra lesz szüksége a kezelőben. Ebben a szakaszban továbbfejleszti a kezelőt, így kevesebb munkát végez, ha a létrehozott sztring nem lesz megírva a naplóba. A paraméterek egy nyilvános API-ra való leképezésének és a kezelő konstruktorának paramétereinek megadására használható System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute . Ez biztosítja a kezelő számára a szükséges információkat annak megállapításához, hogy az interpolált sztringet ki kell-e értékelni.

Kezdjük a kezelő módosításaival. Először adjon hozzá egy mezőt, amely nyomon követi, hogy a kezelő engedélyezve van-e. Adjon hozzá két paramétert a konstruktorhoz: az egyik az üzenet naplószintjének megadásához, a másik pedig a naplóobjektumra mutató hivatkozás:

private readonly bool enabled;

public LogInterpolatedStringHandler(int literalLength, int formattedCount, Logger logger, LogLevel logLevel)
{
    enabled = logger.EnabledLevel >= logLevel;
    builder = new StringBuilder(literalLength);
    Console.WriteLine($"\tliteral length: {literalLength}, formattedCount: {formattedCount}");
}

Ezután használja a mezőt úgy, hogy a kezelő csak a konstansokat vagy formázott objektumokat fűzze hozzá a végső sztring használatakor:

public void AppendLiteral(string s)
{
    Console.WriteLine($"\tAppendLiteral called: {{{s}}}");
    if (!enabled) return;

    builder.Append(s);
    Console.WriteLine($"\tAppended the literal string");
}

public void AppendFormatted<T>(T t)
{
    Console.WriteLine($"\tAppendFormatted called: {{{t}}} is of type {typeof(T)}");
    if (!enabled) return;

    builder.Append(t?.ToString());
    Console.WriteLine($"\tAppended the formatted object");
}

Ezután frissítenie kell a LogMessage deklarációt, hogy a fordító átadja a további paramétereket a kezelő konstruktorának. Ezt a System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute kezelő argumentum használatával kezeli:

public void LogMessage(LogLevel level, [InterpolatedStringHandlerArgument("", "level")] LogInterpolatedStringHandler builder)
{
    if (EnabledLevel < level) return;
    Console.WriteLine(builder.GetFormattedText());
}

Ez az attribútum a megfeleltetendő LogMessage argumentumok listáját adja meg a szükséges literalLength és formattedCount paramétereket követő paraméterekhez. Az üres sztring ("") megadja a fogadót. A fordító a kezelő konstruktorának Logger következő argumentuma által this képviselt objektum értékét helyettesíti. A fordító az alábbi argumentum értékét level helyettesíti. Tetszőleges számú argumentumot megadhat az ön által írt kezelőkhöz. A hozzáadott argumentumok sztringargumentumok.

Ezt a verziót ugyanazzal a tesztkóddal futtathatja. Ezúttal a következő eredmények láthatók:

        literal length: 65, formattedCount: 1
        AppendLiteral called: {Error Level. CurrentTime: }
        Appended the literal string
        AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
        Appended the formatted object
        AppendLiteral called: {. This is an error. It will be printed.}
        Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:19:10 PM. This is an error. It will be printed.
        literal length: 50, formattedCount: 1
        AppendLiteral called: {Trace Level. CurrentTime: }
        AppendFormatted called: {10/20/2021 12:19:10 PM} is of type System.DateTime
        AppendLiteral called: {. This won't be printed.}
Warning Level. This warning is a string, not an interpolated string expression.

Láthatja, hogy a metódusok és AppendFormat a AppendLiteral metódusok meghívása folyamatban van, de nem végeznek semmilyen munkát. A kezelő megállapította, hogy a végleges sztringre nincs szükség, ezért a kezelő nem hozza létre. Még mindig van néhány fejlesztés.

Először is hozzáadhat egy túlterhelést AppendFormatted , amely korlátozza az argumentumot egy implementált típushoz System.IFormattable. Ez a túlterhelés lehetővé teszi, hogy a hívók formázó sztringeket adjanak a helyőrzőkhöz. A módosítás végrehajtása közben változtassuk meg a másik AppendFormatted és AppendLiteral a metódusok visszatérési típusát is ( voidbool ha bármelyik metódus eltérő visszatérési típussal rendelkezik, akkor fordítási hiba jelenik meg). Ez a változás lehetővé teszi a rövidzárolást. A metódusok visszatérése false azt jelzi, hogy az interpolált sztringkifejezés feldolgozását le kell állítani. A true visszatérés azt jelzi, hogy a függvénynek folytatnia kell. Ebben a példában arra használja, hogy leállítja a feldolgozást, ha az eredményül kapott sztringre nincs szükség. A rövidzárolás részletesebb műveleteket támogat. A rögzített hosszúságú pufferek támogatásához leállíthatja a kifejezés feldolgozását egy bizonyos hossz elérése után. Vagy valamilyen feltétel azt jelezheti, hogy a fennmaradó elemekre nincs szükség.

public void AppendFormatted<T>(T t, string format) where T : IFormattable
{
    Console.WriteLine($"\tAppendFormatted (IFormattable version) called: {t} with format {{{format}}} is of type {typeof(T)},");

    builder.Append(t?.ToString(format, null));
    Console.WriteLine($"\tAppended the formatted object");
}

Ezzel a kiegészítéssel formázható sztringeket adhat meg az interpolált sztringkifejezésben:

var time = DateTime.Now;

logger.LogMessage(LogLevel.Error, $"Error Level. CurrentTime: {time}. The time doesn't use formatting.");
logger.LogMessage(LogLevel.Error, $"Error Level. CurrentTime: {time:t}. This is an error. It will be printed.");
logger.LogMessage(LogLevel.Trace, $"Trace Level. CurrentTime: {time:t}. This won't be printed.");

Az :t első üzenet az aktuális idő "rövid időformátumát" adja meg. Az előző példa a kezelő számára létrehozható metódus egyik túlterhelését AppendFormatted mutatta be. Nem kell általános argumentumot megadnia a formázott objektumhoz. A létrehozott típusok sztringgé alakításának hatékonyabb módjai lehetnek. Olyan túlterheléseket AppendFormatted írhat, amelyek általános argumentum helyett ezeket a típusokat is igénybe veszi. A fordító fogja kiválasztani a legjobb túlterhelést. A futtatókörnyezet ezzel a technikával konvertálja sztringkimenetté System.Span<T> . Egész szám paramétert adhat hozzá a kimenet igazításának megadásához, akár anélkül, akár nem IFormattable. A System.Runtime.CompilerServices.DefaultInterpolatedStringHandler .NET 6-tal rendelkező hajók kilenc különböző felhasználású túlterhelést AppendFormatted tartalmaznak. Hivatkozásként használhatja, miközben kezelőt hoz létre az Ön céljaira.

Futtassa a mintát most, és látni fogja, hogy az Trace üzenetben csak az első AppendLiteral lesz meghívva:

        literal length: 60, formattedCount: 1
        AppendLiteral called: Error Level. CurrentTime:
        Appended the literal string
        AppendFormatted called: 10/20/2021 12:18:29 PM is of type System.DateTime
        Appended the formatted object
        AppendLiteral called: . The time doesn't use formatting.
        Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:18:29 PM. The time doesn't use formatting.
        literal length: 65, formattedCount: 1
        AppendLiteral called: Error Level. CurrentTime:
        Appended the literal string
        AppendFormatted (IFormattable version) called: 10/20/2021 12:18:29 PM with format {t} is of type System.DateTime,
        Appended the formatted object
        AppendLiteral called: . This is an error. It will be printed.
        Appended the literal string
Error Level. CurrentTime: 12:18 PM. This is an error. It will be printed.
        literal length: 50, formattedCount: 1
        AppendLiteral called: Trace Level. CurrentTime:
Warning Level. This warning is a string, not an interpolated string expression.

Egyetlen végső frissítést végezhet a kezelő konstruktorán, amely javítja a hatékonyságot. A kezelő hozzáadhat egy végső out bool paramétert. Ha ezt a paramétert false úgy állítja be, hogy azt jelzi, hogy a kezelőt egyáltalán nem kell meghívni az interpolált sztringkifejezés feldolgozásához:

public LogInterpolatedStringHandler(int literalLength, int formattedCount, Logger logger, LogLevel level, out bool isEnabled)
{
    isEnabled = logger.EnabledLevel >= level;
    Console.WriteLine($"\tliteral length: {literalLength}, formattedCount: {formattedCount}");
    builder = isEnabled ? new StringBuilder(literalLength) : default!;
}

Ez a változás azt jelenti, hogy eltávolíthatja a enabled mezőt. Ezután módosíthatja a visszatérési típust AppendLiteral és AppendFormatted a következőt void: . A minta futtatásakor a következő kimenet jelenik meg:

        literal length: 60, formattedCount: 1
        AppendLiteral called: Error Level. CurrentTime:
        Appended the literal string
        AppendFormatted called: 10/20/2021 12:19:10 PM is of type System.DateTime
        Appended the formatted object
        AppendLiteral called: . The time doesn't use formatting.
        Appended the literal string
Error Level. CurrentTime: 10/20/2021 12:19:10 PM. The time doesn't use formatting.
        literal length: 65, formattedCount: 1
        AppendLiteral called: Error Level. CurrentTime:
        Appended the literal string
        AppendFormatted (IFormattable version) called: 10/20/2021 12:19:10 PM with format {t} is of type System.DateTime,
        Appended the formatted object
        AppendLiteral called: . This is an error. It will be printed.
        Appended the literal string
Error Level. CurrentTime: 12:19 PM. This is an error. It will be printed.
        literal length: 50, formattedCount: 1
Warning Level. This warning is a string, not an interpolated string expression.

LogLevel.Trace Csak a konstruktor kimenete adható meg. A kezelő jelezte, hogy nincs engedélyezve, ezért egyik Append metódus sem lett meghívva.

Ez a példa egy fontos pontot mutat be az interpolált sztringkezelők számára, különösen naplózási kódtárak használatakor. Előfordulhat, hogy a helyőrzőkben semmilyen mellékhatás nem jelentkezik. Adja hozzá a következő kódot a fő programhoz, és tekintse meg ezt a viselkedést működés közben:

int index = 0;
int numberOfIncrements = 0;
for (var level = LogLevel.Critical; level <= LogLevel.Trace; level++)
{
    Console.WriteLine(level);
    logger.LogMessage(level, $"{level}: Increment index a few times {index++}, {index++}, {index++}, {index++}, {index++}");
    numberOfIncrements += 5;
}
Console.WriteLine($"Value of index {index}, value of numberOfIncrements: {numberOfIncrements}");

Láthatja, hogy a index változó a ciklus minden iterációjának ötszörösére növekszik. Mivel a helyőrzők kiértékelése csak a következő Error értékekre és szintekre InformationTraceCriticaltörténik, a végleges érték nem felel meg a várt értéknekindex:Warning

Critical
Critical: Increment index a few times 0, 1, 2, 3, 4
Error
Error: Increment index a few times 5, 6, 7, 8, 9
Warning
Warning: Increment index a few times 10, 11, 12, 13, 14
Information
Trace
Value of index 15, value of numberOfIncrements: 25

Az interpolált sztringkezelők nagyobb mértékben szabályozják az interpolált sztringkifejezések sztringgé alakításának módját. A .NET-futtatókörnyezeti csapat már használta ezt a funkciót, hogy több területen javítsa a teljesítményt. Ugyanezt a képességet használhatja a saját kódtáraiban is. A további felfedezéshez tekintse meg a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler. Teljesebb megvalósítást biztosít, mint amit itt készített. Sokkal több túlterhelést fog látni, amelyek a Append metódusok számára lehetségesek.