Est-il possible de formater une chaîne par nom plutôt que par 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.
Est-il possible de faire cela en C #? Dites 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 également acceptable.
Il n'y a pas de méthode intégrée pour gérer cela.
string myString = "{foo} is {bar} and {yadi} is {yada}".Inject(o);
Status.Text = "{UserName} last logged in at {LastLoginDate}".FormatWith(user);
Une troisième méthode améliorée partiellement basée sur les deux précédentes , de Phil Haack
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 résout certains problèmes que ces autres implémentations ont avec échappement par accolade. La poste a des détails. Cela fait aussi l'affaire DataBinder.Eval, mais reste très rapide.
Les deux ont été introduits via le nouveau compilateur Roslyn dans Visual Studio 2015.
C # 6.0:
return "\{someVariable} and also \{someOtherVariable}"
OUreturn $"{someVariable} and also {someOtherVariable}"
source: les nouveautés de C # 6.0
VB 14:
return $"{someVariable} and also {someOtherVariable}"
Fonctionnalités remarquables (dans Visual Studio 2015 IDE):
{index}
fonctionne, mais aussi {(index + 1).ToString().Trim()}
Prendre plaisir! (& cliquez sur "Envoyer un sourire" dans le VS)
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, cela nécessiterait plus de code si vous souhaitez également 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" })
Il ne semble pas y avoir de moyen de le faire en dehors de la boîte. Cependant, il semble possible de mettre en œuvre votre propre IFormatProvider
qui renvoie à une 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);
Les sorties:
FormatProviders
, de sorte que le formatage de texte de fantaisie ne peut pas être utilisé en même temps.
Le cadre lui-même ne fournit pas de moyen de faire cela, mais vous pouvez jeter un oeil à cet article de Scott Hanselman. Exemple d'utilisation:
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);
Ce code de James Newton-King est similaire et fonctionne 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 System.Web.UI.DataBinder pour analyser la chaîne et nécessite de référencer System.Web, ce que certaines personnes n'aiment pas faire dans des applications non Web.
EDIT: Oh, et ils fonctionnent bien avec les types anonymes, si vous n'avez pas d'objet avec les propriétés prêt pour cela:
string name = ...;
DateTime date = ...;
string foo = "{Name} - {Birthday}".FormatWith(new { Name = name, Birthday = date });
Voir https://stackoverflow.com/questions/271398?page=2#358259
Avec l'extension liée à, vous pouvez écrire ceci:
var str = "{foo} {bar} {baz}".Format(foo=>"foo", bar=>2, baz=>new object());
et vous aurez "foo 2 System.Object
".
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 à croire que vous ne trouverez pas vos «variables» nulle part ailleurs dans la chaîne:
string MyString = "{language} has {n} quote types.";
MyString = MyString.Replace("{language}", "C#").Replace("{n}", "1");
Développer 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 pouvez le faire avec Dictionary <string, string> aussi en itérant ses collections .Keys, mais en utilisant List <KeyValuePair <string, string >>, nous pouvons tirer parti de la méthode .ForEach () de List et le condenser un one-liner:
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 les performances .Replace () ne sont pas stellaires lorsqu’elles sont utilisées de manière itérative, car les chaînes en .Net sont immuables. De plus, cela nécessite que la variable MyString
soit définie de manière à ce qu'elle soit accessible au délégué, de sorte qu'elle n'est pas encore parfaite.
Ma bibliothèque open source, Regextra , prend en charge le formatage nommé (entre autres). Il cible actuellement .NET 4.0+ et est disponible sur NuGet . J'ai également un article de blog d'introduction à ce sujet: Regextra: vous aider à réduire vos (problèmes) {2} .
Le bit de formatage nommé prend en charge:
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', passée le 28/02/2014. Votre carte {crédit} sera facturée 1 500,00 $.
Consultez le lien GitHub du projet (ci-dessus) et le wiki pour d'autres exemples.
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));
Les performances sont plutôt bonnes comparées aux autres solutions.
voici une méthode simple pour tout 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 voici comment l'utiliser:
DateTime date = DateTime.Now;
string dateString = StringWithFormat("{Month}/{Day}/{Year}", date);
sortie: 27/02/2012
Je doute que cela sera possible. La première chose qui me vient à l’esprit est de savoir comment vous allez avoir accès aux noms de variables locales.
Il pourrait toutefois y avoir un moyen intelligent d’utiliser les expressions LINQ et Lambda pour le faire.
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, le 11/05/2011, le 18 août {100.40}
En voici une que j'ai faite il y a quelque temps. Il étend String avec une méthode Format prenant un seul argument. La bonne chose est qu’elle utilisera la chaîne standard.Format si vous fournissez un argument simple comme un int, mais si vous utilisez quelque chose comme un type anonyme, cela fonctionnera aussi.
Exemple d'utilisation:
"The {Name} family has {Children} children".Format(new { Children = 4, Name = "Smith" })
Cela se traduirait par "La famille Smith a 4 enfants."
Il ne fait pas des choses de liaison fous comme les tableaux et les indexeurs. Mais c'est super simple et 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;
}
}
}
J'ai résolu ce problème d'une manière légèrement différente des solutions existantes . Il effectue l'essentiel du remplacement de l'élément nommé (et non le bit de réflexion que certains ont déjà fait). C’est extrêmement simple et rapide .... Voici 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!"));
}
J'espère que quelqu'un trouve cela utile!
J'ai implémenté il s'agit d'une classe simple qui duplique les fonctionnalités de String.Format (sauf lors de l'utilisation de classes). Vous pouvez utiliser un dictionnaire ou un type pour définir des champs.
https://github.com/SergueiFedorov/NamedFormatString
C # 6.0 ajoute cette fonctionnalité directement dans la spécification de langue, donc NamedFormatString
est destiné à la compatibilité ascendante.
Même si la réponse acceptée donne de bons exemples, le .Inject ainsi que certains exemples de Haack ne gèrent pas l'échappement. Nombre d'entre eux s'appuient également fortement sur Regex (plus lent) ou sur DataBinder.Eval, qui n'est pas disponible sur .NET Core et dans certains autres environnements.
Dans cet esprit, j'ai écrit un analyseur basé sur une machine à états simple qui parcourt les caractères, écrivant dans une sortie StringBuilder
, caractère par caractère. Elle est implémentée en tant que méthode (s) d'extension String
et peut prendre un Dictionary<string, object>
ou object
avec des paramètres en entrée (à l'aide de la réflexion).
Il gère un nombre illimité de {{{escaping}}}
et jette FormatException
lorsque l'entrée contient des accolades non é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ésume en 10 états principaux - Par exemple, lorsque la machine à états est en dehors d’un crochet, le caractère suivant est un accolade ouverte, un accolade ouvert échappé, un accolade fermée ou un personnage ordinaire. Chacune de ces conditions est traitée individuellement au fur et à mesure de l'avancement de la boucle, en ajoutant des caractères à une sortie StringBuffer
ou à une clé StringBuffer
. Lorsqu'un paramètre est fermé, la valeur de la clé StringBuffer
est utilisée pour rechercher la valeur du paramètre dans le dictionnaire, qui est ensuite poussée dans la sortie StringBuffer
. A la fin, la valeur de la sortie StringBuffer
est renvoyée.