1

I could've sworn I saw some articles a while ago about imperfect but useful string interpolation methods for C, but no such luck right now. However, there's Razor, which does more or less what I want.

Suppose you have a database client application with tickets, and e-mail notifications are to be sent whenever tickets get created, significant parameters change, etc. The user would like to customize the wording of those notification e-mails, which would be easiest using string interpolation, i.e. accessing various properties of the ticket object from within the string, like so:

Dear @user,

the ticket @ticket.ID (@ticket.URL) has changed in priority from @previousTicket.priority to @currentTicket.priority.

What I'd like is a method that I pass various objects (in this case user, oldTicket and ticket), and have it evaluate the string and get the necessary properties through reflection.

4

4 に答える 4

2

単純な置換ステップを使用して、単純なキーワード置換機能を実現できます。

{0}キーワードを、{1}などに置き換えてstring.Format、適切な場所で適切なパラメーターを使用するだけです。

Dictionary<string, int> keywords = new Dictionary<string, int>();
keywords["@user"] = 0;
keywords["@ticket.ID"] = 1;
keywords["@ticket.URL"] = 2;
// etc...
string template = @"Dear @user,

the ticket @ticket.ID (@ticket.URL) has changed in priority from @previousTicket.priority to @currentTicket.priority.";

string replacedTemplate = template;
foreach (var keyword in keywords)
{
    replacedTemplate = replacedTemplate.Replace(keyword.Key, "{" + keyword.Value + "}");
}
string formattedMessage = string.Format(replacedTemplate, userName, ticket.ID, ticket.URL); // corresponding to the dictionary

これは、適切に定義された限られた量のキーワードがあることを前提としています。

于 2010-10-23T07:47:06.680 に答える
1

これを行うエンジンはたくさんあると思いますが、私たちは Castle NVelocity に落ち着きました。

http://www.castleproject.org/others/nvelocity/usingit.html

名前と値のペアを介してデータを受け取り、テンプレートを介して実行します。メモリ内のあらゆる種類のテキスト出力を生成するために使用できます。インクルード、条件付きセクション、および繰り返しデータ (注文の行など) もサポートしています。

最も重要なことは、非常に使いやすいことです。

于 2010-10-23T07:46:25.320 に答える
0

参照: http://nuget.org/List/Packages/Expansive

于 2011-10-11T15:24:36.933 に答える
0

私は2つの答えを見たことがなかったので、先に進んで独自の実装を行いました:

using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace StringInterpolation {
    /// <summary>
    /// An object with an explicit, available-at-runtime name.
    /// </summary>
    public struct NamedObject {
        public string Name;
        public object Object;

        public NamedObject(string name, object obj) {
            Name = name;
            Object = obj;
        }
    }

    public static class StringInterpolation {
        /// <summary>
        /// Parses a string for basic Razor-like interpolation with explicitly passed objects.
        /// For example, pass a NamedObject user, and you can use @user and @user.SomeProperty in your string.
        /// </summary>
        /// <param name="s">The string to be parsed.</param>
        /// <param name="objects">A NamedObject array for objects too allow for parsing.</param>
        public static string Interpolate(this string s, params NamedObject[] objects) {
            System.Diagnostics.Debug.WriteLine(s);

            List<NamedObject> namedObjects = new List<NamedObject>(objects);

            Dictionary<NamedObject, Dictionary<string, string>> objectsWithProperties = new Dictionary<NamedObject, Dictionary<string, string>>();

            foreach (NamedObject no in objects) {
                Dictionary<string, string> properties = new Dictionary<string, string>();

                foreach (System.Reflection.PropertyInfo pInfo in no.Object.GetType().GetProperties())
                    properties.Add(pInfo.Name, pInfo.GetValue(no.Object, new object[] { }).ToString());

                objectsWithProperties.Add(no, properties);
            }

            foreach (Match match in Regex.Matches(s, @"@(\w+)(\.(\w+))?")) {
                NamedObject no;
                no = namedObjects.Find(delegate(NamedObject n) { return n.Name == match.Groups[1].Value; });

                if (no.Name != null && match.Groups.Count == 4)
                    if (string.IsNullOrEmpty(match.Groups[3].Value))
                        s = s.Replace(match.Value, no.Object.ToString());
                    else {
                        Dictionary<string, string> properties = null;
                        string value;
                        objectsWithProperties.TryGetValue(no, out properties);

                        if (properties != null && properties.TryGetValue(match.Groups[3].Value, out value))
                            s = s.Replace(match.Value, value);
                    }

            }

            return s;
        }
    }
}

そして、ここにテストがあります:

using StringInterpolation;

namespace StringInterpolationTest {
    class User {
        public string Name { get; set; }
    }

    class Ticket {
        public string ID { get; set; }
        public string Priority { get; set; }
    }

    class Program {
        static void Main(string[] args) {
            User user = new User();
            user.Name = "Joe";
            Ticket previousTicket = new Ticket();
            previousTicket.ID = "1";
            previousTicket.Priority = "Low";
            Ticket currentTicket = new Ticket();
            currentTicket.ID = "1";
            currentTicket.Priority = "High";

            System.Diagnostics.Debug.WriteLine("User: @user, Username: @user.Name, Previous ticket priority: @previousTicket.Priority, New priority: @currentTicket.Priority".Interpolate(
                new NamedObject("user", user),
                new NamedObject("previousTicket", previousTicket),
                new NamedObject("currentTicket", currentTicket)
            ));
        }
    }
}

これは Albin のバリアントよりもかなり多くのコードですが、ID を手動で設定する必要はありません (ただし、潜在的な補間のためにどのオブジェクトを「エクスポート」するかを事前に知っておく必要があります)。

みんなありがとう!

于 2010-10-23T08:39:09.403 に答える