Benannte Zeichenfolgenformatierung in C #

Gibt es eine Möglichkeit, eine Zeichenfolge nach Name lieber als Position in C # zu formatieren?

In Python kann ich etwas wie dieses Beispiel (schamlos von hier gestohlen) tun:

>>> print '%(language)s has %(#)03d quote types.' % \ {'language': "Python", "#": 2} Python has 002 quote types. 

Gibt es eine Möglichkeit, dies in C # zu tun? Sprich zum Beispiel:

 String.Format("{some_variable}: {some_other_variable}", ...); 

In der Lage, dies mit einem Variablennamen zu tun, wäre nett, aber ein Wörterbuch ist auch akzeptabel.

   

Es gibt keine eingebaute Methode, um dies zu handhaben.

Hier ist eine Methode

 string myString = "{foo} is {bar} and {yadi} is {yada}".Inject(o); 

Hier ist ein anderes

 Status.Text = "{UserName} last logged in at {LastLoginDate}".FormatWith(user); 

Ein drittes verbessertes Verfahren, teilweise basierend auf den beiden obigen , von Phil Haack

Ich habe eine Implementierung, die ich gerade in meinem Blog hier gepostet habe: http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx

Es behandelt einige Probleme, die diese anderen Implementierungen mit geschweiften Klammern haben. Die Post hat Details. Es tut das DataBinder.Eval Ding auch, aber ist immer noch sehr schnell.

Sie können auch anonyme Typen wie diese verwenden:

  public string Format(string input, object p) { foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(p)) input = input.Replace("{" + prop.Name + "}", (prop.GetValue(p) ?? "(null)").ToString()); return input; } 

Natürlich würde es mehr Code erfordern, wenn Sie auch die Formatierung analysieren möchten, aber Sie können eine Zeichenfolge mit dieser function wie folgt formatieren:

 Format("test {first} and {another}", new { first = "something", another = "something else" }) 

Interpolierte Zeichenfolgen wurden in C # 6.0 und Visual Basic 14 hinzugefügt

Beide wurden durch den neuen Roslyn- Compiler in Visual Studio 2015 eingeführt .

  • C # 6.0:

    return "\{someVariable} and also \{someOtherVariable}" ODER
    return $"{someVariable} and also {someOtherVariable}"

    • Quelle: Was ist neu in C # 6.0?

  • VB 14:

    return $"{someVariable} and also {someOtherVariable}"

    • Quelle: Was ist neu in VB 14?

Bemerkenswerte Features (in Visual Studio 2015 IDE):

  • Syntax Färbung wird unterstützt – Variablen in Strings sind hervorgehoben
  • Refactoring wird unterstützt – beim Umbenennen werden auch in Strings enthaltene Variablen umbenannt
  • Tatsächlich werden nicht nur Variablennamen, sondern Ausdrücke unterstützt – zB funktioniert nicht nur {index} , sondern auch {(index + 1).ToString().Trim()}

Genießen! (& klicken Sie auf “Senden Sie ein Lächeln” in der VS)

Es scheint keinen Weg zu geben, dies out-of-the-box zu tun. Es IFormatProvider möglich, einen eigenen IFormatProvider zu implementieren, der mit einem IDictionary für Werte verknüpft ist.

 var Stuff = new Dictionary { { "language", "Python" }, { "#", 2 } }; var Formatter = new DictionaryFormatProvider(); // Interpret {0:x} where {0}=IDictionary and "x" is hash key Console.WriteLine string.Format(Formatter, "{0:language} has {0:#} quote types", Stuff); 

Ausgänge:

  Python hat 2 Anführungszeichen 

Der Nachteil ist, dass Sie FormatProviders nicht mischen FormatProviders , so dass die Formatierung von Text nicht gleichzeitig verwendet werden kann.

Das Framework selbst bietet keine Möglichkeit, dies zu tun, aber Sie können sich diesen Beitrag von Scott Hanselman ansehen. Beispielverwendung:

 Person p = new Person(); string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}"); Assert.AreEqual("$3.43 Hanselman, {ScottName} 1/22/1974 12:00:00 AM", foo); 

Dieser Code von James Newton-King ist ähnlich und arbeitet mit Untereigenschaften und Indizes,

 string foo = "Top result for {Name} was {Results[0].Name}".FormatWith(student)); 

Der Code von James beruht auf System.Web.UI.DataBinder , um die Zeichenfolge zu analysieren, und erfordert die Referenzierung von System.Web, was manche Personen in nicht webbasierten Anwendungen nicht tun möchten.

EDIT: Oh, und sie funktionieren gut mit anonymen Typen, wenn Sie kein Objekt mit Eigenschaften dafür haben:

 string name = ...; DateTime date = ...; string foo = "{Name} - {Birthday}".FormatWith(new { Name = name, Birthday = date }); 

Siehe https://stackoverflow.com/questions/271398?page=2#358259

Mit der Erweiterung linked-to können Sie dies schreiben:

 var str = "{foo} {bar} {baz}".Format(foo=>"foo", bar=>2, baz=>new object()); 

und du wirst "foo 2 System.Object ” bekommen.

Ich denke, das nächste, was Sie bekommen, ist ein indiziertes Format:

 String.Format("{0} has {1} quote types.", "C#", "1"); 

Es gibt auch String.Replace (), wenn Sie es in mehreren Schritten tun wollen, und glauben Sie, dass Sie Ihre “Variablen” nirgendwo sonst in der Zeichenfolge finden werden:

 string MyString = "{language} has {n} quote types."; MyString = MyString.Replace("{language}", "C#").Replace("{n}", "1"); 

Erweitern, um eine Liste zu verwenden:

 List> replacements = GetFormatDictionary(); foreach (KeyValuePair item in replacements) { MyString = MyString.Replace(item.Key, item.Value); } 

Sie können dies auch mit einem Dictionary tun, indem Sie die .Keys-Sammlungen iterieren, aber indem Sie eine List > verwenden, können wir die .ForEach () -Methode der List nutzen und sie wieder zusammenfassen ein One-Liner:

 replacements.ForEach(delegate(KeyValuePair) item) { MyString = MyString.Replace(item.Key, item.Value);}); 

Ein Lambda wäre noch einfacher, aber ich bin immer noch auf .Net 2.0. Beachten Sie auch, dass die .Replace () – performance nicht stellar ist, wenn sie iterativ verwendet wird, da Strings in .Net unveränderlich sind. Außerdem muss die MyString Variable so definiert sein, dass sie für den Delegaten zugänglich ist. MyString ist sie noch nicht perfekt.

Meine Open-Source-Bibliothek Regextra unterstützt (unter anderem) die Formatierung von Namen. Es zielt derzeit auf .NET 4.0 und ist auf NuGet verfügbar. Ich habe auch einen einleitenden Blogpost darüber: Regextra: hilft Ihnen, Ihre (Probleme) zu reduzieren {2} .

Das benannte Formatierungsbit unterstützt:

  • Grundlegende Formatierung
  • Verschachtelte Eigenschaften formatieren
  • Wörterbuchformatierung
  • Flucht der Trennzeichen
  • Standard / Benutzerdefiniert / IFormatProvider Zeichenfolge formatieren

Beispiel:

 var order = new { Description = "Widget", OrderDate = DateTime.Now, Details = new { UnitPrice = 1500 } }; string template = "We just shipped your order of '{Description}', placed on {OrderDate:d}. Your {{credit}} card will be billed {Details.UnitPrice:C}."; string result = Template.Format(template, order); // or use the extension: template.FormatTemplate(order); 

Ergebnis:

Wir haben Ihre Bestellung von “Widget” am 28.02.2014 versandt. Ihre {credit} Karte wird mit $ 1.500,00 abgerechnet.

Weitere Beispiele finden Sie im GitHub-Link des Projekts (oben) und im Wiki.

Überprüfen Sie diese:

 public static string StringFormat(string format, object source) { var matches = Regex.Matches(format, @"\{(.+?)\}"); List keys = (from Match matche in matches select matche.Groups[1].Value).ToList(); return keys.Aggregate( format, (current, key) => { int colonIndex = key.IndexOf(':'); return current.Replace( "{" + key + "}", colonIndex > 0 ? DataBinder.Eval(source, key.Substring(0, colonIndex), "{0:" + key.Substring(colonIndex + 1) + "}") : DataBinder.Eval(source, key).ToString()); }); } 

Probe:

 string format = "{foo} is a {bar} is a {baz} is a {qux:#.#} is a really big {fizzle}"; var o = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.Now }; Console.WriteLine(StringFormat(format, o)); 

Die performance ist im Vergleich zu anderen Lösungen ziemlich gut.

Ich bezweifle, dass dies möglich sein wird. Das erste, was Ihnen in den Sinn kommt, ist, wie Sie auf lokale Variablennamen zugreifen können.

Es könnte jedoch auch einige clevere Wege geben, LINQ- und Lambda-Ausdrücke zu verwenden.

Hier ist eine, die ich vor einiger Zeit gemacht habe. Es erweitert String mit einer Format-Methode, die ein einzelnes Argument verwendet. Das nette Ding ist, dass es das Standardstring.Format benutzt, wenn Sie ein einfaches Argument wie ein int zur Verfügung stellen, aber wenn Sie etwas wie anonymen Typ verwenden, funktioniert es auch.

Beispielverwendung:

 "The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" }) 

Würde dazu führen, “Die Familie Smith hat 4 Kinder.”

Es macht keine verrückten Bindungen wie Arrays und Indexer. Aber es ist super einfach und leistungsstark.

  public static class AdvancedFormatString { ///  /// An advanced version of string.Format. If you pass a primitive object (string, int, etc), it acts like the regular string.Format. If you pass an anonmymous type, you can name the paramters by property name. ///  ///  ///  ///  ///  /// "The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" }) /// /// results in /// "This Smith family has 4 children ///  public static string Format(this string formatString, object arg, IFormatProvider format = null) { if (arg == null) return formatString; var type = arg.GetType(); if (Type.GetTypeCode(type) != TypeCode.Object || type.IsPrimitive) return string.Format(format, formatString, arg); var properties = TypeDescriptor.GetProperties(arg); return formatString.Format((property) => { var value = properties[property].GetValue(arg); return Convert.ToString(value, format); }); } public static string Format(this string formatString, Func formatFragmentHandler) { if (string.IsNullOrEmpty(formatString)) return formatString; Fragment[] fragments = GetParsedFragments(formatString); if (fragments == null || fragments.Length == 0) return formatString; return string.Join(string.Empty, fragments.Select(fragment => { if (fragment.Type == FragmentType.Literal) return fragment.Value; else return formatFragmentHandler(fragment.Value); }).ToArray()); } private static Fragment[] GetParsedFragments(string formatString) { Fragment[] fragments; if ( parsedStrings.TryGetValue(formatString, out fragments) ) { return fragments; } lock (parsedStringsLock) { if ( !parsedStrings.TryGetValue(formatString, out fragments) ) { fragments = Parse(formatString); parsedStrings.Add(formatString, fragments); } } return fragments; } private static Object parsedStringsLock = new Object(); private static Dictionary parsedStrings = new Dictionary(StringComparer.Ordinal); const char OpeningDelimiter = '{'; const char ClosingDelimiter = '}'; ///  /// Parses the given format string into a list of fragments. ///  ///  ///  static Fragment[] Parse(string format) { int lastCharIndex = format.Length - 1; int currFragEndIndex; Fragment currFrag = ParseFragment(format, 0, out currFragEndIndex); if (currFragEndIndex == lastCharIndex) { return new Fragment[] { currFrag }; } List fragments = new List(); while (true) { fragments.Add(currFrag); if (currFragEndIndex == lastCharIndex) { break; } currFrag = ParseFragment(format, currFragEndIndex + 1, out currFragEndIndex); } return fragments.ToArray(); } ///  /// Finds the next delimiter from the starting index. ///  static Fragment ParseFragment(string format, int startIndex, out int fragmentEndIndex) { bool foundEscapedDelimiter = false; FragmentType type = FragmentType.Literal; int numChars = format.Length; for (int i = startIndex; i < numChars; i++) { char currChar = format[i]; bool isOpenBrace = currChar == OpeningDelimiter; bool isCloseBrace = isOpenBrace ? false : currChar == ClosingDelimiter; if (!isOpenBrace && !isCloseBrace) { continue; } else if (i < (numChars - 1) && format[i + 1] == currChar) {//{{ or }} i++; foundEscapedDelimiter = true; } else if (isOpenBrace) { if (i == startIndex) { type = FragmentType.FormatItem; } else { if (type == FragmentType.FormatItem) throw new FormatException("Two consequtive unescaped { format item openers were found. Either close the first or escape any literals with another {."); //curr character is the opening of a new format item. so we close this literal out string literal = format.Substring(startIndex, i - startIndex); if (foundEscapedDelimiter) literal = ReplaceEscapes(literal); fragmentEndIndex = i - 1; return new Fragment(FragmentType.Literal, literal); } } else {//close bracket if (i == startIndex || type == FragmentType.Literal) throw new FormatException("A } closing brace existed without an opening { brace."); string formatItem = format.Substring(startIndex + 1, i - startIndex - 1); if (foundEscapedDelimiter) formatItem = ReplaceEscapes(formatItem);//a format item with a { or } in its name is crazy but it could be done fragmentEndIndex = i; return new Fragment(FragmentType.FormatItem, formatItem); } } if (type == FragmentType.FormatItem) throw new FormatException("A format item was opened with { but was never closed."); fragmentEndIndex = numChars - 1; string literalValue = format.Substring(startIndex); if (foundEscapedDelimiter) literalValue = ReplaceEscapes(literalValue); return new Fragment(FragmentType.Literal, literalValue); } ///  /// Replaces escaped brackets, turning '{{' and '}}' into '{' and '}', respectively. ///  ///  ///  static string ReplaceEscapes(string value) { return value.Replace("{{", "{").Replace("}}", "}"); } private enum FragmentType { Literal, FormatItem } private class Fragment { public Fragment(FragmentType type, string value) { Type = type; Value = value; } public FragmentType Type { get; private set; } ///  /// The literal value, or the name of the fragment, depending on fragment type. ///  public string Value { get; private set; } } } 
 private static Regex s_NamedFormatRegex = new Regex(@"\{(?!\{)(?[\w]+)(:(?(\{\{|\}\}|[^\{\}])*)?)?\}", RegexOptions.Compiled); public static StringBuilder AppendNamedFormat(this StringBuilder builder,IFormatProvider provider, string format, IDictionary args) { if (builder == null) throw new ArgumentNullException("builder"); var str = s_NamedFormatRegex.Replace(format, (mt) => { string key = mt.Groups["key"].Value; string fmt = mt.Groups["fmt"].Value; object value = null; if (args.TryGetValue(key,out value)) { return string.Format(provider, "{0:" + fmt + "}", value); } else { return mt.Value; } }); builder.Append(str); return builder; } public static StringBuilder AppendNamedFormat(this StringBuilder builder, string format, IDictionary args) { if (builder == null) throw new ArgumentNullException("builder"); return builder.AppendNamedFormat(null, format, args); } 

Beispiel:

 var builder = new StringBuilder(); builder.AppendNamedFormat( @"你好,{Name},今天是{Date:yyyy/MM/dd}, 这是你第{LoginTimes}次登录,积分{Score:{{ 0.00 }}}", new Dictionary() { { "Name", "wayjet" }, { "LoginTimes",18 }, { "Score", 100.4 }, { "Date",DateTime.Now } }); 

Ausgabe: 你好, wayjet, 是 是 2011-05-04, 这 是 你 第 18 次 登录, 积分 {100.40}

Hier ist eine einfache Methode für jedes Objekt:

  using System.Text.RegularExpressions; using System.ComponentModel; public static string StringWithFormat(string format, object args) { Regex r = new Regex(@"\{([A-Za-z0-9_]+)\}"); MatchCollection m = r.Matches(format); var properties = TypeDescriptor.GetProperties(args); foreach (Match item in m) { try { string propertyName = item.Groups[1].Value; format = format.Replace(item.Value, properties[propertyName].GetValue(args).ToString()); } catch { throw new FormatException("The format string is not valid"); } } return format; } 

Und hier, wie man es benutzt:

  DateTime date = DateTime.Now; string dateString = StringWithFormat("{Month}/{Day}/{Year}", date); 

Ausgabe: 27.02.2012

Ich implementierte das ist eine einfache class, die die functionalität von String.Format dupliziert (außer bei Verwendung von classn). Sie können entweder ein Wörterbuch oder einen Typ verwenden, um Felder zu definieren.

https://github.com/SergueiFedorov/NamedFormatString

C # 6.0 fügt diese functionalität direkt in die Sprachspezifikation ein, sodass NamedFormatString Abwärtskompatibilität funktioniert.

Ich triggerse das auf eine etwas andere Weise als die bestehenden Lösungen. Es macht den core der genannten Item Ersetzung (nicht das reflectionsbit, das einige getan haben). Es ist extrem schnell und einfach … Das ist meine Lösung:

 ///  /// Formats a string with named format items given a template dictionary of the items values to use. ///  public class StringTemplateFormatter { private readonly IFormatProvider _formatProvider; ///  /// Constructs the formatter with the specified . /// This is defaulted to CultureInfo.CurrentCulture if none is provided. ///  ///  public StringTemplateFormatter(IFormatProvider formatProvider = null) { _formatProvider = formatProvider ?? CultureInfo.CurrentCulture; } ///  /// Formats a string with named format items given a template dictionary of the items values to use. ///  /// The text template /// The named values to use as replacements in the formatted string. /// The resultant text string with the template values replaced. public string FormatTemplate(string text, Dictionary templateValues) { var formattableString = text; var values = new List(); foreach (KeyValuePair value in templateValues) { var index = values.Count; formattableString = ReplaceFormattableItem(formattableString, value.Key, index); values.Add(value.Value); } return String.Format(_formatProvider, formattableString, values.ToArray()); } ///  /// Convert named string template item to numbered string template item that can be accepted by String.Format ///  /// The string containing the named format item /// The name of the format item /// The index to use for the item value /// The formattable string with the named item substituted with the numbered format item. private static string ReplaceFormattableItem(string formattableString, string itemName, int index) { return formattableString .Replace("{" + itemName + "}", "{" + index + "}") .Replace("{" + itemName + ",", "{" + index + ",") .Replace("{" + itemName + ":", "{" + index + ":"); } } 

Es wird folgendermaßen verwendet:

  [Test] public void FormatTemplate_GivenANamedGuid_FormattedWithB_ShouldFormatCorrectly() { // Arrange var template = "My guid {MyGuid:B} is awesome!"; var templateValues = new Dictionary { { "MyGuid", new Guid("{A4D2A7F1-421C-4A1D-9CB2-9C2E70B05E19}") } }; var sut = new StringTemplateFormatter(); // Act var result = sut.FormatTemplate(template, templateValues); //Assert Assert.That(result, Is.EqualTo("My guid {a4d2a7f1-421c-4a1d-9cb2-9c2e70b05e19} is awesome!")); } 

Hoffe jemand findet das nützlich!

Obwohl die angenommene Antwort einige gute Beispiele liefert, behandeln das .Inject sowie einige der Haack-Beispiele nicht das Entkommen. Viele sind auch stark auf Regex (langsamer) oder DataBinder.Eval angewiesen, das in .NET Core und in einigen anderen Umgebungen nicht verfügbar ist.

Vor diesem Hintergrund habe ich einen einfachen Parser auf State-Machine-Basis geschrieben, der Zeichen durchströmt und Zeichen für Zeichen in eine StringBuilder Ausgabe schreibt. Sie wird als String Erweiterungsmethode (n) implementiert und kann sowohl ein Dictionary als auch ein object mit Parametern als Eingabe (mit Reflektion) verwenden.

Es behandelt unbegrenzte Ebenen von {{{escaping}}} FormatException {{{escaping}}} und FormatException wenn die Eingabe unausgeglichene Klammern und / oder andere Fehler enthält.

 public static class StringExtension { ///  /// Extension method that replaces keys in a string with the values of matching object properties. ///  /// The format string, containing keys like {foo} and {foo:SomeFormat}. /// The object whose properties should be injected in the string /// A version of the formatString string with keys replaced by (formatted) key values. public static string FormatWith(this string formatString, object injectionObject) { return formatString.FormatWith(GetPropertiesDictionary(injectionObject)); } ///  /// Extension method that replaces keys in a string with the values of matching dictionary entries. ///  /// The format string, containing keys like {foo} and {foo:SomeFormat}. /// An  with keys and values to inject into the string /// A version of the formatString string with dictionary keys replaced by (formatted) key values. public static string FormatWith(this string formatString, IDictionary dictionary) { char openBraceChar = '{'; char closeBraceChar = '}'; return FormatWith(formatString, dictionary, openBraceChar, closeBraceChar); } ///  /// Extension method that replaces keys in a string with the values of matching dictionary entries. ///  /// The format string, containing keys like {foo} and {foo:SomeFormat}. /// An  with keys and values to inject into the string /// A version of the formatString string with dictionary keys replaced by (formatted) key values. public static string FormatWith(this string formatString, IDictionary dictionary, char openBraceChar, char closeBraceChar) { string result = formatString; if (dictionary == null || formatString == null) return result; // start the state machine! // ballpark output string as two times the length of the input string for performance (avoids reallocating the buffer as often). StringBuilder outputString = new StringBuilder(formatString.Length * 2); StringBuilder currentKey = new StringBuilder(); bool insideBraces = false; int index = 0; while (index < formatString.Length) { if (!insideBraces) { // currently not inside a pair of braces in the format string if (formatString[index] == openBraceChar) { // check if the brace is escaped if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) { // add a brace to the output string outputString.Append(openBraceChar); // skip over braces index += 2; continue; } else { // not an escaped brace, set state to inside brace insideBraces = true; index++; continue; } } else if (formatString[index] == closeBraceChar) { // handle case where closing brace is encountered outside braces if (index < formatString.Length - 1 && formatString[index + 1] == closeBraceChar) { // this is an escaped closing brace, this is okay // add a closing brace to the output string outputString.Append(closeBraceChar); // skip over braces index += 2; continue; } else { // this is an unescaped closing brace outside of braces. // throw a format exception throw new FormatException($"Unmatched closing brace at position {index}"); } } else { // the character has no special meaning, add it to the output string outputString.Append(formatString[index]); // move onto next character index++; continue; } } else { // currently inside a pair of braces in the format string // found an opening brace if (formatString[index] == openBraceChar) { // check if the brace is escaped if (index < formatString.Length - 1 && formatString[index + 1] == openBraceChar) { // there are escaped braces within the key // this is illegal, throw a format exception throw new FormatException($"Illegal escaped opening braces within a parameter - index: {index}"); } else { // not an escaped brace, we have an unexpected opening brace within a pair of braces throw new FormatException($"Unexpected opening brace inside a parameter - index: {index}"); } } else if (formatString[index] == closeBraceChar) { // handle case where closing brace is encountered inside braces // don't attempt to check for escaped braces here - always assume the first brace closes the braces // since we cannot have escaped braces within parameters. // set the state to be outside of any braces insideBraces = false; // jump over brace index++; // at this stage, a key is stored in current key that represents the text between the two braces // do a lookup on this key string key = currentKey.ToString(); // clear the stringbuilder for the key currentKey.Clear(); object outObject; if (!dictionary.TryGetValue(key, out outObject)) { // the key was not found as a possible replacement, throw exception throw new FormatException($"The parameter \"{key}\" was not present in the lookup dictionary"); } // we now have the replacement value, add the value to the output string outputString.Append(outObject); // jump to next state continue; } // if } else { // character has no special meaning, add it to the current key currentKey.Append(formatString[index]); // move onto next character index++; continue; } // else } // if inside brace } // while // after the loop, if all braces were balanced, we should be outside all braces // if we're not, the input string was misformatted. if (insideBraces) { throw new FormatException("The format string ended before the parameter was closed."); } return outputString.ToString(); } ///  /// Creates a Dictionary from an objects properties, with the Key being the property's /// name and the Value being the properties value (of type object) ///  /// An object who's properties will be used /// A  of property values  private static Dictionary GetPropertiesDictionary(object properties) { Dictionary values = null; if (properties != null) { values = new Dictionary(); PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties); foreach (PropertyDescriptor prop in props) { values.Add(prop.Name, prop.GetValue(properties)); } } return values; } } 

Letztendlich läuft die gesamte Logik in 10 Hauptstadien zusammen – denn wenn sich die Zustandsmaschine außerhalb einer Klammer befindet und sich ebenfalls in einer Klammer befindet, ist das nächste Zeichen entweder eine offene Klammer, eine geflickte offene Klammer, eine geschlossene Klammer, eine entkommene geschlossene Klammer, oder ein gewöhnlicher Charakter. Jede dieser Bedingungen wird im Verlauf der Schleife einzeln behandelt, indem Zeichen entweder einem Ausgabe- StringBuffer oder einem Schlüssel- StringBuffer hinzugefügt werden. Wenn ein Parameter geschlossen ist, wird der Wert des Schlüssels StringBuffer verwendet, um den Wert des Parameters im Wörterbuch nachzuschlagen, der dann in den Ausgabe- StringBuffer geschoben wird. Am Ende wird der Wert der Ausgabe StringBuffer zurückgegeben.

 string language = "Python"; int numquotes = 2; string output = language + " has "+ numquotes + " language types."; 

Edit: Was ich hätte sagen sollen, war: “Nein, ich glaube nicht, was Sie tun möchten, wird von C # unterstützt. Das ist so nah wie Sie bekommen werden.”