1

我可以发誓不久前我看到了一些关于 C 的不完美但有用的字符串插值方法的文章,但现在没有这样的运气。但是,有 Razor,它或多或少可以满足我的需求。

假设您有一个带有工单的数据库客户端应用程序,并且每当创建工单、重大参数更改等时都会发送电子邮件通知。用户希望自定义这些通知电子邮件的措辞,这将是最容易使用的字符串插值,即从字符串中访问对象的各种属性ticket,如下所示:

亲爱@user的,

票证@ticket.ID( @ticket.URL) 的优先级已从 更改@previousTicket.priority@currentTicket.priority

我想要的是一种方法,我传递各种对象(在这种情况下useroldTicketticket),并让它评估字符串并通过反射获得必要的属性。

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

我没有看到这两个答案,所以我继续进行自己的实现:

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 回答
0

另请参阅: http: //nuget.org/List/Packages/Expansive

于 2011-10-11T15:24:36.933 回答