C# 在 .NET 中进行字符串模板化的好方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/733378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's a good way of doing string templating in .NET?
提问by Simon
I need to send email notifications to users and I need to allow the admin to provide a template for the message body (and possibly headers, too).
我需要向用户发送电子邮件通知,并且我需要允许管理员为邮件正文(也可能是标题)提供模板。
I'd like something like string.Format
that allows me to give named replacement strings, so the template can look like this:
我想要类似的东西string.Format
,允许我给出命名的替换字符串,所以模板看起来像这样:
Dear {User},
Your job finished at {FinishTime} and your file is available for download at {FileURL}.
Regards,
--
{Signature}
What's the simplest way for me to do that?
对我来说最简单的方法是什么?
采纳答案by Anton Gogolev
Use a templating engine. StringTemplateis one of those, and there are many.
使用模板引擎。StringTemplate就是其中之一,而且还有很多。
回答by thinkbeforecoding
If you need something very powerful (but really not the simplest way) you can host ASP.NET and use it as your templating engine.
如果您需要非常强大的东西(但实际上不是最简单的方法),您可以托管 ASP.NET 并将其用作模板引擎。
You'll have all the power of ASP.NET to format the body of your message.
您将拥有 ASP.NET 的所有功能来格式化消息正文。
回答by Ovi
You could use string.Replace(...), eventually in a for-each through all the keywords. If there are only a few keywords you can have them on a line like this:
您可以使用 string.Replace(...),最终通过所有关键字在 for-each 中使用。如果只有几个关键字,您可以将它们放在这样的一行中:
string myString = template.Replace("FirstName", "John").Replace("LastName", "Smith").Replace("FinishTime", DateTime.Now.ToShortDateString());
Or you could use Regex.Replace(...), if you need something a bit more powerful and with more options.
或者你可以使用 Regex.Replace(...),如果你需要更强大的东西和更多的选择。
Read this article on codeprojectto view which string replacement option is fastest for you.
阅读有关 codeproject 的这篇文章,以查看哪种字符串替换选项对您来说最快。
回答by TcKs
You can use the "string.Format" method:
您可以使用“string.Format”方法:
var user = GetUser();
var finishTime = GetFinishTime();
var fileUrl = GetFileUrl();
var signature = GetSignature();
string msg =
@"Dear {0},
Your job finished at {1} and your file is available for download at {2}.
Regards,
--
{3}";
msg = string.Format(msg, user, finishTime, fileUrl, signature);
It allows you to change the content in the future and is friendly for localization.
它允许您将来更改内容,并且对本地化很友好。
回答by 0100110010101
Actually, you can use XSLT. You create a simple XML template:
实际上,您可以使用 XSLT。您创建一个简单的 XML 模板:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="TETT">
<p>
Dear <xsl:variable name="USERNAME" select="XML_PATH" />,
Your job finished at <xsl:variable name="FINISH_TIME" select="XML_PATH" /> and your file is available for download at <xsl:variable name="FILE_URL" select="XML_PATH" />.
Regards,
--
<xsl:variable name="SIGNATURE" select="XML_PATH" />
</p>
</xsl:template>
Then create a XmlDocument to perform transformation against: XmlDocument xmlDoc = new XmlDocument();
然后创建一个 XmlDocument 来执行转换: XmlDocument xmlDoc = new XmlDocument();
XmlNode xmlNode = xmlDoc .CreateNode(XmlNodeType.Element, "EMAIL", null);
XmlElement xmlElement= xmlDoc.CreateElement("USERNAME");
xmlElement.InnerXml = username;
xmlNode .AppendChild(xmlElement); ///repeat the same thing for all the required fields
xmlDoc.AppendChild(xmlNode);
After that, apply the transformation:
之后,应用转换:
XPathNavigator xPathNavigator = xmlDocument.DocumentElement.CreateNavigator();
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
XmlTextWriter xmlWriter = new XmlTextWriter(sw);
your_xslt_transformation.Transform(xPathNavigator, null, xmlWriter);
return sb.ToString();
回答by Martin Peck
Implementing your own custom formatter might be a good idea.
实现您自己的自定义格式化程序可能是一个好主意。
Here's how you do it. First, create a type that defines the stuff you want to inject into your message. Note: I'm only going to illustrate this with the User part of your template...
这是你如何做到的。首先,创建一个类型来定义要注入到消息中的内容。注意:我只会用模板的用户部分来说明这一点......
class JobDetails
{
public string User
{
get;
set;
}
}
Next, implement a simple custom formatter...
接下来,实现一个简单的自定义格式化程序...
class ExampleFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
return this;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
// make this more robust
JobDetails job = (JobDetails)arg;
switch (format)
{
case "User":
{
return job.User;
}
default:
{
// this should be replaced with logic to cover the other formats you need
return String.Empty;
}
}
}
}
Finally, use it like this...
最后,像这样使用它......
string template = "Dear {0:User}. Your job finished...";
JobDetails job = new JobDetails()
{
User = "Martin Peck"
};
string message = string.Format(new ExampleFormatter(), template, job);
... which will generate the text "Dear Martin Peck. Your job finished...".
...这将生成文本“亲爱的马丁派克。你的工作完成了......”。
回答by epitka
If you are coding in VB.NET you can use XML literals. If you are coding in C# you can use ShartDevelop to have files in VB.NET in the same project as C# code.
如果您在 VB.NET 中编码,则可以使用 XML 文字。如果您使用 C# 进行编码,则可以使用 ShatDevelop 在与 C# 代码相同的项目中使用 VB.NET 中的文件。
回答by Benjamin Gruenbaum
Here is the version for those of you who can use a new version of C#:
以下是适用于可以使用新版本 C# 的人的版本:
// add $ at start to mark string as template
var template = $"Your job finished at {FinishTime} and your file is available for download at {FileURL}."
In a line - this is now a fully supported language feature (string interpolation).
总之——这是现在完全支持的语言功能(字符串插值)。
回答by DaveRandom
A very simple regex-based solution. Supports \n
-style single character escape sequences and {Name}
-style named variables.
一个非常简单的基于正则表达式的解决方案。支持\n
-style 单字符转义序列和{Name}
-style 命名变量。
Source
来源
class Template
{
/// <summary>Map of replacements for characters prefixed with a backward slash</summary>
private static readonly Dictionary<char, string> EscapeChars
= new Dictionary<char, string>
{
['r'] = "\r",
['n'] = "\n",
['\'] = "\",
['{'] = "{",
};
/// <summary>Pre-compiled regular expression used during the rendering process</summary>
private static readonly Regex RenderExpr = new Regex(@"\.|{([a-z0-9_.\-]+)}",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>Template string associated with the instance</summary>
public string TemplateString { get; }
/// <summary>Create a new instance with the specified template string</summary>
/// <param name="TemplateString">Template string associated with the instance</param>
public Template(string TemplateString)
{
if (TemplateString == null) {
throw new ArgumentNullException(nameof(TemplateString));
}
this.TemplateString = TemplateString;
}
/// <summary>Render the template using the supplied variable values</summary>
/// <param name="Variables">Variables that can be substituted in the template string</param>
/// <returns>The rendered template string</returns>
public string Render(Dictionary<string, object> Variables)
{
return Render(this.TemplateString, Variables);
}
/// <summary>Render the supplied template string using the supplied variable values</summary>
/// <param name="TemplateString">The template string to render</param>
/// <param name="Variables">Variables that can be substituted in the template string</param>
/// <returns>The rendered template string</returns>
public static string Render(string TemplateString, Dictionary<string, object> Variables)
{
if (TemplateString == null) {
throw new ArgumentNullException(nameof(TemplateString));
}
return RenderExpr.Replace(TemplateString, Match => {
switch (Match.Value[0]) {
case '\':
if (EscapeChars.ContainsKey(Match.Value[1])) {
return EscapeChars[Match.Value[1]];
}
break;
case '{':
if (Variables.ContainsKey(Match.Groups[1].Value)) {
return Variables[Match.Groups[1].Value].ToString();
}
break;
}
return string.Empty;
});
}
}
Usage
用法
var tplStr1 = @"Hello {Name},\nNice to meet you!";
var tplStr2 = @"This {Type} \{contains} \ some things \n that shouldn't be rendered";
var variableValues = new Dictionary<string, object>
{
["Name"] = "Bob",
["Type"] = "string",
};
Console.Write(Template.Render(tplStr1, variableValues));
// Hello Bob,
// Nice to meet you!
var template = new Template(tplStr2);
Console.Write(template.Render(variableValues));
// This string {contains} \ some things \n that shouldn't be rendered
Notes
笔记
- I've only defined
\n
,\r
,\\
and\{
escape sequences and hard-coded them. You could easily add more or make them definable by the consumer. - I've made variable names case-insensitive, as things like this are often presented to end-users/non-programmers and I don't personally think that case-sensitivity make sense in that use-case - it's just one more thing they can get wrong and phone you up to complain about (plus in general if you think you need case sensitive symbol names what you really need are better symbol names). To make them case-sensitive, simply remove the
RegexOptions.IgnoreCase
flag. - I strip invalid variable names and escape sequences from the result string. To leave them intact, return
Match.Value
instead of the empty string at the end of theRegex.Replace
callback. You could also throw an exception. - I've used
{var}
syntax, but this may interfere with the native interpolated string syntax. If you want to define templates in string literals in you code, it might be advisable to change the variable delimiters to e.g.%var%
(regex\\.|%([a-z0-9_.\-]+)%
) or some other syntax of your choosing which is more appropriate to the use case.
- 我只定义
\n
,\r
,\\
和\{
转义序列和硬编码他们。您可以轻松添加更多内容或让消费者可以定义它们。 - 我让变量名不区分大小写,因为这样的事情经常呈现给最终用户/非程序员,我个人不认为区分大小写在那个用例中有意义 - 这只是他们的另一件事可能会出错并打电话给您抱怨(通常,如果您认为需要区分大小写的符号名称,那么您真正需要的是更好的符号名称)。要使它们区分大小写,只需删除
RegexOptions.IgnoreCase
标志。 - 我从结果字符串中去除无效的变量名和转义序列。要保持它们不变,请
Match.Value
在Regex.Replace
回调结束时返回而不是空字符串。您也可以抛出异常。 - 我使用过
{var}
语法,但这可能会干扰本机内插字符串语法。如果您想在代码中以字符串文字定义模板,建议将变量分隔符更改为 eg%var%
(regex\\.|%([a-z0-9_.\-]+)%
) 或您选择的更适合用例的其他一些语法。
回答by mrrrk
Building on Benjamin Gruenbaum's answer, in C# version 6 you can add a @ with the $ and pretty much use your code as it is, e.g.:
以 Benjamin Gruenbaum 的回答为基础,在 C# 版本 6 中,您可以添加带有 $ 的 @,并且几乎可以原样使用您的代码,例如:
var text = $@"Dear {User},
Your job finished at {FinishTime} and your file is available for download at {FileURL}.
Regards,
--
{Signature}
";
The $
is for string interpolation: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
该$
是字符串插值:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
The @
is the verbatim identifier: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim
这@
是逐字标识符:https: //docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim
...and you can use these in conjunction.
...你可以结合使用这些。
:o)
:o)