9 Stimmen

C# Benannte Parameter in eine Zeichenkette, die die Parameterwerte ersetzt

Ich möchte in eine gute Leistung Weise (ich hoffe) ersetzen einen benannten Parameter in meinem String zu einem benannten Parameter aus Code, Beispiel, meine Zeichenfolge:

"Hi {name}, do you like milk?"

Wie könnte ich den {Name} durch Code, reguläre Ausdrücke ersetzen? Zu teuer? Welche Methode empfehlen Sie?

Wie ersetzen sie im Beispiel NHibernates HQL :my_param durch den benutzerdefinierten Wert? Oder in ASP.NET (MVC) Routing, das ich besser mag, "{controller}/{action}", new { controller = "Hello", ... }?

20voto

Konrad Rudolph Punkte 503837

Haben Sie festgestellt, dass reguläre Ausdrücke zu teuer sind?

Die Kosten für reguläre Ausdrücke sind stark übertrieben. Für ein so einfaches Muster ist die Leistung recht gut, wahrscheinlich sogar nur geringfügig schlechter als das direkte Suchen und Ersetzen. Haben Sie auch schon mit dem Compiled bei der Erstellung des regulären Ausdrucks?

Aber können Sie nicht einfach die einfachste Methode verwenden, d. h. Replace ?

string varname = "name";
string pattern = "{" + varname + "}";
Console.WriteLine("Hi {name}".Replace(pattern, "Mike"));

1 Stimmen

(Obwohl Sie Replace(pattern, "Mike") meinen)

5 Stimmen

@Jon: Danke. Warum einen Compiler benutzen, wenn man einen Jon hat? ;-)

14voto

Marc Gravell Punkte 970173

Regex ist sicherlich eine praktikable Option, insbesondere mit einer MatchEvaluator :

    Regex re = new Regex(@"\{(\w*?)\}", RegexOptions.Compiled); // store this...

    string input = "Hi {name}, do you like {food}?";

    Dictionary<string, string> vals = new Dictionary<string, string>();
    vals.Add("name", "Fred");
    vals.Add("food", "milk");

    string q = re.Replace(input, delegate(Match match)
    {
        string key = match.Groups[1].Value;
        return vals[key];
    });

0 Stimmen

Verdammt... Wie konntest du das 18 Minuten vor mir bekommen?

0 Stimmen

Wenn Sie .NET 3.5 verwenden, können Sie das Schlüsselwort delegate ausschalten. delegate(Match match) kann match => sein.

0 Stimmen

@scalvert - um genau zu sein, ist das C# 3.0, nicht .NET 3.5; es würde auch funktionieren, wenn man .NET 2.0 mit C# 3.0 anvisieren würde.

4voto

James Curran Punkte 98228

Wenn Sie nun Ihre Ersetzungen in einem Wörterbuch haben, etwa so:

    var  replacements = new Dictionary<string, string>();
    replacements["name"] = "Mike";
    replacements["age"]= "20";

dann wird die Regex ganz einfach:

Regex regex = new Regex(@"\{(?<key>\w+)\}");
    string formattext = "{name} is {age} years old";
    string newStr = regex.Replace(formattext, 
            match=>replacements[match.Groups[1].Captures[0].Value]);

1 Stimmen

+1 für die Kürze - von allen beigetragenen Lösungen ist dies mein persönlicher Favorit :-)

0 Stimmen

Ich hasse Regexe, aber das hier ist ganz nett.

2voto

mindplay.dk Punkte 6764

Nachdem ich darüber nachgedacht hatte, wurde mir klar, dass ich mir eigentlich wünschte, dass String.Format() ein IDictionary als Argument nehmen würde, und dass Vorlagen mit Namen statt mit Indizes geschrieben werden könnten.

Bei Stringsubstitutionen mit vielen möglichen Schlüsseln/Werten führen die Indexnummern zu unleserlichen String-Templates - und in manchen Fällen weiß man nicht einmal, welche Elemente welche Nummer haben werden, also habe ich mir die folgende Erweiterung ausgedacht:

https://gist.github.com/896724

Grundsätzlich können Sie damit String-Vorlagen mit Namen anstelle von Zahlen und ein Wörterbuch anstelle eines Arrays verwenden, und Sie haben alle anderen guten Eigenschaften von String.Format(), so dass die Verwendung eines benutzerdefinierten IFormatProvider, wenn nötig, und ermöglicht die Verwendung aller üblichen Formatierung Syntax - Präzision, Länge, etc.

Le site Beispiel im Referenzmaterial für String.Format ist ein großartiges Beispiel dafür, wie Vorlagen mit vielen nummerierten Elementen völlig unleserlich werden - portiert man dieses Beispiel, um diese neue Erweiterungsmethode zu verwenden, erhält man etwas wie dieses:

var replacements = new Dictionary<String, object>()
                       {
                           { "date1", new DateTime(2009, 7, 1) },
                           { "hiTime", new TimeSpan(14, 17, 32) },
                           { "hiTemp", 62.1m },
                           { "loTime", new TimeSpan(3, 16, 10) },
                           { "loTemp", 54.8m }
                       };

var template =
    "Temperature on {date1:d}:\n{hiTime,11}: {hiTemp} degrees (hi)\n{loTime,11}: {loTemp} degrees (lo)";

var result = template.Subtitute(replacements);

Wie bereits erwähnt, sollten Sie so etwas nicht verwenden, wenn Ihr Text stark optimiert werden muss. Wenn Sie auf diese Weise Millionen von Zeichenketten in einer Schleife formatieren müssen, kann der Speicher- und Leistungs-Overhead erheblich sein.

Andererseits, wenn Sie darauf bedacht sind, lesbaren, wartbaren Code zu schreiben - und wenn Sie, sagen wir, eine Reihe von Datenbankoperationen durchführen, wird diese Funktion im Großen und Ganzen keinen bedeutenden Overhead hinzufügen.

...

Der Einfachheit halber habe ich versucht, eine Methode hinzuzufügen, die anstelle eines Wörterbuchs ein anonymes Objekt akzeptiert:

public static String Substitute(this String template, object obj)
{
    return Substitute(
        template,
        obj.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(obj, null))
    );
}

Aus irgendeinem Grund funktioniert dies nicht - die Übergabe eines anonymen Objekts wie new { name: "value" } zu dieser Erweiterungsmethode gibt eine Kompilierzeit-Fehlermeldung, die besagt, dass die beste Übereinstimmung die IDictionary-Version dieser Methode war. Ich bin nicht sicher, wie man das beheben kann. (jemand?)

0 Stimmen

Haben Sie jemals den anonymen Typ dafür herausgefunden? Das wäre wirklich cool.

0 Stimmen

Ich kann mich nicht erinnern - das ist schon lange her, und ich arbeite derzeit nicht mit ASP.NET... Ich habe mir den Code von damals angesehen, und er ging in Produktion und sah genauso aus - ich erinnere mich vage daran, dass das Problem irgendwie nicht mit diesem Code, sondern mit dem Consumer-Code zusammenhing. Das ist der beste Anhaltspunkt, den ich Ihnen geben kann.

1voto

sosdude Punkte 27

Wie wäre es mit

stringVar = "Hello, {0}. How are you doing?";
arg1 = "John";    // or args[0]
String.Format(stringVar, arg1)

Sie können sogar mehrere Args haben, indem Sie einfach die {x} erhöhen und der Methode Format() einen weiteren Parameter hinzufügen. Ich bin mir nicht sicher, was der Unterschied ist, aber sowohl "String" als auch "String" haben diese Methode.

1 Stimmen

Er braucht benannte Parameter, keine geordneten Parameter.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X