Formatage de chaîne nommé en C#

y a-t-il un moyen de formater une chaîne de caractères par son nom plutôt que par sa position en C#?

en python, je peux faire quelque chose comme cet exemple (volé sans vergogne de ici ):

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

y a-t-il un moyen de le faire en C#? Dis par exemple:

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

être capable de faire cela en utilisant un nom de variable serait bien, mais un dictionnaire est acceptable aussi.

153
demandé sur Robert Harvey 2008-10-01 22:30:04

18 réponses

Il n'y a pas de méthode intégrée pour gérer cela.

Voici une méthode

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

En voici un autre

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

une troisième méthode améliorée partiellement basée sur les deux ci-dessus , de Phil Haack

125
répondu John Sheehan 2009-01-05 04:36:40

j'ai une implémentation que je viens de poster sur mon blog ici: http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx

il aborde certains problèmes que ces autres implémentations ont avec brace Escape. La poste a de détails. Il ne le DataBinder.Eval chose, mais est encore très rapide.

45
répondu Haacked 2009-01-05 04:32:05

vous pouvez également utiliser des types anonymes comme ceci:

    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;
    }

bien sûr, il faudrait plus de code si vous voulez aussi analyser le formatage, mais vous pouvez formater une chaîne en utilisant cette fonction comme:

Format("test {first} and {another}", new { first = "something", another = "something else" })
39
répondu Doggett 2012-06-19 23:54:12

Interpolated strings ont été ajoutés dans C# 6.0 et Visual Basic 14

tous deux ont été introduits par le nouveau compilateur Roslyn dans Visual Studio 2015 .

  • C# 6.0:

    return "\{someVariable} and also \{someOtherVariable}" ou

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

  • VB 14:

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

caractéristiques Remarquables (dans Visual Studio 2015 IDE):

  • la coloration syntaxique est supportée - les variables contenues dans les chaînes sont surlignées
  • refactoring est supporté - lors du renommage, les variables contenues dans les chaînes sont renommées, aussi
  • en fait non seulement les noms de variables, mais les expressions sont prises en charge - par exemple non seulement {index} des œuvres, mais aussi {(index + 1).ToString().Trim()}

Profitez-en! (& cliquez sur "Envoyer un sourire" dans le VS)

37
répondu miroxlav 2016-03-30 06:17:55

il ne semble pas y avoir de solution. Cependant, il semble possible de mettre en œuvre votre propre IFormatProvider qui se lie à un IDictionary pour les valeurs.

var Stuff = new Dictionary<string, object> {
   { "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);

sorties:

Python has 2 quote types

la mise en garde est que vous ne pouvez pas mélanger FormatProviders , de sorte que le formatage de texte de fantaisie ne peut pas être utilisé en même temps.

12
répondu spoulson 2008-10-01 19:01:41

Le cadre lui-même ne fournit pas un moyen de faire cela, mais vous pouvez prendre un coup d'oeil à ce post par Scott, Hanselman. Exemple d'utilisation:

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

ce code de James Newton-King est similaire et travaille avec des sous-Propriétés et des index,

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

le code de James s'appuie sur le système .Web.INTERFACE.DataBinder pour analyser la chaîne de caractères et nécessite un système de référencement.Web, certaines personnes n'aiment pas faire dans les applications non web.

EDIT: Oh, et ils fonctionnent bien avec les types anonymes, si vous n'avez pas un objet avec des propriétés prêt pour cela:

string name = ...;
DateTime date = ...;
string foo = "{Name} - {Birthday}".FormatWith(new { Name = name, Birthday = date });
9
répondu Lucas 2008-10-01 19:56:17

je pense que le plus proche que vous obtiendrez est un format indexé:

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

il y a aussi String.Replace (), si vous êtes prêt à le faire en plusieurs étapes et à le faire en croyant que vous ne trouverez pas vos 'variables' ailleurs dans la chaîne:

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

extension de ceci pour utiliser une liste:

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

vous pourriez faire cela avec un dictionnaire aussi en itérant c'est .Les collections de clés, mais en utilisant un List > nous pouvons profiter de la liste.Méthode ForEach() et la condenser à nouveau sur une doublure:

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

un lambda serait encore plus simple, mais je suis toujours sur .Net 2.0. Notez également que l' .Les performances de Replace () ne sont pas stellaires lorsqu'elles sont utilisées de manière itérative, puisque les chaînes dans .Net sont immuables. De plus, il faut que la variable MyString soit définie de manière à ce qu'elle soit accessible au délégué, donc elle n'est pas encore parfaite.

4
répondu Joel Coehoorn 2008-10-01 19:12:59

Ma bibliothèque open source, Regextra , prend en charge le formatage nommé (entre autres choses). Il cible actuellement .NET 4.0+ et est disponible sur NuGet . J'ai également un billet de blog d'introduction à ce sujet: Regextra: vous aider à réduire vos (problèmes){2} .

le bit de formatage nommé supporte:

  • mise en forme de Base
  • propriétés imbriquées mise en forme
  • formatage du dictionnaire
  • évasion des délimiteurs
  • Standard/Personnalisé/IFormatProvider mise en forme de chaîne

exemple:

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);

résultat:

nous venons d'expédier votre commande de 'Widget', placé le 28/02/2014. Votre carte {de crédit} sera facturée $1,500.00.

Vérifier le le lien GitHub du projet (ci-dessus) et wiki pour d'autres exemples.

3
répondu Ahmad Mageed 2014-04-19 17:37:48

Vérifier celui-ci:

public static string StringFormat(string format, object source)
{
    var matches = Regex.Matches(format, @"\{(.+?)\}");
    List<string> 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());
        });
}

échantillon:

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));
La Performance

est assez bonne par rapport à d'autres solutions.

2
répondu Pavlo Neyman 2011-09-22 05:53:17

je doute que cela soit possible. La première chose qui vient à l'esprit est comment allez-vous obtenir l'accès à des noms de variables locales?

il pourrait y avoir une façon intelligente D'utiliser les expressions LINQ et Lambda pour le faire cependant.

1
répondu leppie 2008-10-01 18:33:37

J'en ai fait un il y a longtemps. Il étend la chaîne de caractères avec une méthode de Format prenant un seul argument. Ce qui est bien, c'est qu'il utilise la chaîne standard.Formater si vous fournissez un argument simple comme un int, mais si vous utilisez quelque chose comme le type anonyme il fonctionnera aussi.

exemple d'usage:

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

se traduirait par "la famille Smith a 4 enfants."

il ne fait pas des trucs fou liant comme les tableaux et indexeur. Mais c'est très simple et très performant.

    public static class AdvancedFormatString
{

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="formatString"></param>
    /// <param name="arg"></param>
    /// <returns></returns>
    /// <example>
    /// "The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })
    /// 
    /// results in 
    /// "This Smith family has 4 children
    /// </example>
    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<string, string> 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<string,Fragment[]> parsedStrings = new Dictionary<string,Fragment[]>(StringComparer.Ordinal);

    const char OpeningDelimiter = '{';
    const char ClosingDelimiter = '}';

    /// <summary>
    /// Parses the given format string into a list of fragments.
    /// </summary>
    /// <param name="format"></param>
    /// <returns></returns>
    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<Fragment> fragments = new List<Fragment>();
        while (true)
        {
            fragments.Add(currFrag);
            if (currFragEndIndex == lastCharIndex)
            {
                break;
            }
            currFrag = ParseFragment(format, currFragEndIndex + 1, out currFragEndIndex);
        }
        return fragments.ToArray();

    }

    /// <summary>
    /// Finds the next delimiter from the starting index.
    /// </summary>
    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);

    }

    /// <summary>
    /// Replaces escaped brackets, turning '{{' and '}}' into '{' and '}', respectively.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    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;
        }

        /// <summary>
        /// The literal value, or the name of the fragment, depending on fragment type.
        /// </summary>
        public string Value
        {
            get;
            private set;
        }


    }

}
1
répondu Steve Potter 2011-01-29 18:51:11
private static Regex s_NamedFormatRegex = new Regex(@"\{(?!\{)(?<key>[\w]+)(:(?<fmt>(\{\{|\}\}|[^\{\}])*)?)?\}", RegexOptions.Compiled);

public static StringBuilder AppendNamedFormat(this StringBuilder builder,IFormatProvider provider, string format, IDictionary<string, object> 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<string, object> args)
{
    if (builder == null) throw new ArgumentNullException("builder");
    return builder.AppendNamedFormat(null, format, args);
}

exemple:

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

sortie: Փ, wayjet, փ2011-05-04, փi, փi, փi, փi, փi, փi, փi, փi, փi, փi, փi, փi, փi"

1
répondu wayjet 2011-05-04 06:56:44

voici une méthode simple pour n'importe quel objet:

    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;
    }

et ici comment l'utiliser:

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

sortie : 2/27/2012

1
répondu Ashkan Ghodrat 2012-02-27 21:16:25

j'ai implémenté cette classe simple qui duplique les fonctionnalités de String.Format (sauf pour l'utilisation de classes). Vous pouvez utiliser un dictionnaire ou un type pour définir les champs.

https://github.com/SergueiFedorov/NamedFormatString

C# 6.0 ajoute cette fonctionnalité directement dans le langage spec, donc NamedFormatString est pour la compatibilité ascendante.

0
répondu Serguei Fedorov 2015-05-14 21:06:47

j'ai résolu ce problème d'une manière légèrement différente pour les solutions existantes. Il fait le noyau de l'article de remplacement nommé (pas le bit de réflexion que certains ont fait). Il est extrêmement rapide et simple... C'est ma solution:

/// <summary>
/// Formats a string with named format items given a template dictionary of the items values to use.
/// </summary>
public class StringTemplateFormatter
{
    private readonly IFormatProvider _formatProvider;

    /// <summary>
    /// Constructs the formatter with the specified <see cref="IFormatProvider"/>.
    /// This is defaulted to <see cref="CultureInfo.CurrentCulture">CultureInfo.CurrentCulture</see> if none is provided.
    /// </summary>
    /// <param name="formatProvider"></param>
    public StringTemplateFormatter(IFormatProvider formatProvider = null)
    {
        _formatProvider = formatProvider ?? CultureInfo.CurrentCulture;
    }

    /// <summary>
    /// Formats a string with named format items given a template dictionary of the items values to use.
    /// </summary>
    /// <param name="text">The text template</param>
    /// <param name="templateValues">The named values to use as replacements in the formatted string.</param>
    /// <returns>The resultant text string with the template values replaced.</returns>
    public string FormatTemplate(string text, Dictionary<string, object> templateValues)
    {
        var formattableString = text;
        var values = new List<object>();
        foreach (KeyValuePair<string, object> value in templateValues)
        {
            var index = values.Count;
            formattableString = ReplaceFormattableItem(formattableString, value.Key, index);
            values.Add(value.Value);
        }
        return String.Format(_formatProvider, formattableString, values.ToArray());
    }

    /// <summary>
    /// Convert named string template item to numbered string template item that can be accepted by <see cref="string.Format(string,object[])">String.Format</see>
    /// </summary>
    /// <param name="formattableString">The string containing the named format item</param>
    /// <param name="itemName">The name of the format item</param>
    /// <param name="index">The index to use for the item value</param>
    /// <returns>The formattable string with the named item substituted with the numbered format item.</returns>
    private static string ReplaceFormattableItem(string formattableString, string itemName, int index)
    {
        return formattableString
            .Replace("{" + itemName + "}", "{" + index + "}")
            .Replace("{" + itemName + ",", "{" + index + ",")
            .Replace("{" + itemName + ":", "{" + index + ":");
    }
}

Il est utilisé de la manière suivante:

    [Test]
    public void FormatTemplate_GivenANamedGuid_FormattedWithB_ShouldFormatCorrectly()
    {
        // Arrange
        var template = "My guid {MyGuid:B} is awesome!";
        var templateValues = new Dictionary<string, object> { { "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!"));
    }

Espère que quelqu'un trouve cette pratique!

0
répondu Mark Whitfeld 2015-11-19 11:10:36

même si la réponse acceptée donne de bons exemples, le .Injectez ainsi que certains des exemples de Haack ne gèrent pas la fuite. Beaucoup comptent aussi beaucoup sur Regex (plus lent), ou DataBinder.Eval qui n'est pas disponible sur .NET Core, et dans d'autres environnements.

avec cela à l'esprit, j'ai écrit un analyseur basé sur une machine d'état simple qui diffuse à travers les caractères, en écrivant à une sortie StringBuilder , caractère par caractère. Il est mis en œuvre comme String méthode(s) d'extension et peut prendre à la fois un Dictionary<string, object> ou object avec paramètres en entrée (en utilisant la réflexion).

il gère les niveaux illimités de {{{escaping}}} et lance FormatException lorsque l'entrée contient des bretelles déséquilibrées et/ou d'autres erreurs.

public static class StringExtension {
    /// <summary>
    /// Extension method that replaces keys in a string with the values of matching object properties.
    /// </summary>
    /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
    /// <param name="injectionObject">The object whose properties should be injected in the string</param>
    /// <returns>A version of the formatString string with keys replaced by (formatted) key values.</returns>
    public static string FormatWith(this string formatString, object injectionObject) {
        return formatString.FormatWith(GetPropertiesDictionary(injectionObject));
    }

    /// <summary>
    /// Extension method that replaces keys in a string with the values of matching dictionary entries.
    /// </summary>
    /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
    /// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
    /// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
    public static string FormatWith(this string formatString, IDictionary<string, object> dictionary) {
        char openBraceChar = '{';
        char closeBraceChar = '}';

        return FormatWith(formatString, dictionary, openBraceChar, closeBraceChar);
    }
        /// <summary>
        /// Extension method that replaces keys in a string with the values of matching dictionary entries.
        /// </summary>
        /// <param name="formatString">The format string, containing keys like {foo} and {foo:SomeFormat}.</param>
        /// <param name="dictionary">An <see cref="IDictionary"/> with keys and values to inject into the string</param>
        /// <returns>A version of the formatString string with dictionary keys replaced by (formatted) key values.</returns>
    public static string FormatWith(this string formatString, IDictionary<string, object> 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();
    }

    /// <summary>
    /// 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)
    /// </summary>
    /// <param name="properties">An object who's properties will be used</param>
    /// <returns>A <see cref="Dictionary"/> of property values </returns>
    private static Dictionary<string, object> GetPropertiesDictionary(object properties) {
        Dictionary<string, object> values = null;
        if (properties != null) {
            values = new Dictionary<string, object>();
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(properties);
            foreach (PropertyDescriptor prop in props) {
                values.Add(prop.Name, prop.GetValue(properties));
            }
        }
        return values;
    }
}

en fin de Compte, toute la logique se réduit en 10 états principaux - pour quand la machine d'état est en dehors d'un crochet et aussi à l'intérieur d'un crochet, le caractère suivant est soit ouvert d'une accolade, un échappé ouvrir accolade, une accolade fermée, un échappé fermé accolade, ou un caractère ordinaire. Chacune de ces conditions est traitée individuellement au fur et à mesure de la progression de la boucle, en ajoutant des caractères à une sortie StringBuffer ou à une touche StringBuffer . Lorsqu'un paramètre est fermée, la valeur de la clé StringBuffer est utilisée pour rechercher la valeur du paramètre dans le dictionnaire, qui est ensuite poussé vers la sortie StringBuffer . À la fin, la valeur de la sortie StringBuffer est renvoyé.

0
répondu Ryan 2016-02-23 03:08:16
string language = "Python";
int numquotes = 2;
string output = language + " has "+ numquotes + " language types.";

Edit: Ce que j'aurais dû dire était, "Non, Je ne crois pas que ce que vous voulez faire soit soutenu par C#. C'est aussi proche que vous allez obtenir."

-6
répondu Kevin 2008-10-01 18:51:37