$在字符串之前是什么意思?


250

我打算使用逐字字符串,但我错误地键入$而不是@

但是编译器没有给我任何错误,并且编译成功。

我想知道它是什么,它做什么。我搜索了它,但找不到任何东西。

但是,它不像逐字字符串,因为我不会写:

string str = $"text\";

有谁知道$字符串在C#中代表什么。

string str = $"text";

我正在使用Visual Studio 2015 CTP。

Answers:


418

$String.FormatC#6的新功能,它是字符串插值的简写形式,并且与字符串插值一起使用。在您的情况下,它什么也不string.Format()做,就像什么也不做一样。

当用于参考其他值来构建字符串时,它是独立存在的。以前必须这样写:

var anInt = 1;
var aBool = true;
var aString = "3";
var formated = string.Format("{0},{1},{2}", anInt, aBool, aString);

现在变成:

var anInt = 1;
var aBool = true;
var aString = "3";
var formated = $"{anInt},{aBool},{aString}";

还有一种替代方法-不太为人所知-使用字符串内插的形式$@ (两个符号的顺序很重要)。它允许@""混合字符串的功能$""以支持字符串插值,而不需要\\整个字符串。所以下面两行:

var someDir = "a";
Console.WriteLine($@"c:\{someDir}\b\c");

将输出:

c:\a\b\c

29
请注意,它并不是真正使用String.Format,而是基于编译器的功能,而不是运行时的功能。
沙哈尔·普里什

2
较小的注意事项,我今天学到的,如果您使用$@,则必须使用来转义"字符""。仅使用时不是这种情况$
平的

3
@Flater与$符号无关。这与$符号存在之前的行为相同。
BVernon

2
就您而言,逐字(@)和插值($)符号的顺序很重要,在C#8中已对此进行了更正,因此该顺序不再重要。参见:devsanon.com/uncategorized/…–
elkaz

38

它创建一个插值字符串

MSDN

用于构造字符串。内插的字符串表达式看起来像包含表达式的模板字符串。内插的字符串表达式通过将包含的表达式替换为表达式结果的ToString表示形式来创建字符串。

例如:

 var name = "Sam";
 var msg = $"hello, {name}";

 Console.WriteLine(msg); // hello, Sam

您可以在插值字符串中使用表达式

 var msg = $"hello, {name.ToLower()}";
 Console.WriteLine(msg); // hello, sam

这样做的好处是,您不必像处理一样担心参数的顺序String.Format

  var s = String.Format("{0},{1},{2}...{88}",p0,p1,..,p88);

现在,如果要删除一些参数,则必须更新所有计数,现在不再是这种情况了。

请注意,string.format如果您想在自己的网站中指定文化信息,则旧的还是有用的格式中有用的


请注意,$如果您$使用正确的区域性(例如)将数据转换为表达式内部的字符串,则可能仍可以使用并指定区域性信息{somevar.ToString(...,[Insert culture info here])}
jrh

18

范例程式码

public class Person {
    public String firstName { get; set; }
    public String lastName { get; set; }
}

// Instantiate Person
var person = new Person { firstName = "Albert", lastName = "Einstein" };

// We can print fullname of the above person as follows
Console.WriteLine("Full-Name - " + person.firstName + " " + person.lastName);
Console.WriteLine("Full-Name - {0} {1}", person.firstName, person.lastName);
Console.WriteLine($"Full-Name - {person.firstName} {person.lastName}");

输出量

Full-Name - Albert Einstein
Full-Name - Albert Einstein
Full-Name - Albert Einstein

它是插值字符串。您可以在任何可以使用字符串文字的地方使用内插字符串。在运行程序时,将使用插值字符串文字执行代码,该代码通过评估插值表达式来计算新的字符串文字。每次执行带有插值字符串的代码时,都会执行此计算。

以下示例生成一个字符串值,其中所有字符串插值均已计算。这是最终结果,类型为字符串。所有出现的双大括号都会(“{{“ and “}}”)转换为单个大括号。

string text = "World";
var message = $"Hello, {text}";

执行以上两行后,变量message包含“ Hello,World”。

Console.WriteLine(message); // Prints Hello, World

参考- -MSDN


10

很酷的功能。我只想指出重点,如果某些人不知道为什么它比string.format更好。

我读到有人说将string.format排序为“ {0} {1} {2}”以匹配参数。您不必强制以string.format顺序订购“ {0} {1} {2}”,也可以执行“ {2} {0} {1}”。但是,如果您有很多参数(例如20),则您确实希望将字符串排序为“ {0} {1} {2} ... {19}”。如果是乱七八糟的混乱,您将很难排定参数。

使用$,您可以内联添加参数而无需计算参数。这使代码更易于阅读和维护。

$的缺点是,您不能轻易在字符串中重复该参数,而必须键入它。例如,如果您厌倦了键入System.Environment.NewLine,则可以执行string.format(“ ... {0} ... {0} ... {0}”,System.Environment.NewLine),但是,以$为单位,您必须重复该操作。您无法执行$“ {0}”并将其传递给string.format,因为$“ {0}”返回“ 0”。

在旁注中,我阅读了另一篇重复的tpoic文章中的评论。我无法评论,所以在这里。他说过

string msg = n + " sheep, " + m + " chickens";

创建多个字符串对象。实际上这是不正确的。如果您在一行中执行此操作,它将仅创建一个字符串并将其放置在字符串缓存中。

1) string + string + string + string;
2) string.format()
3) stringBuilder.ToString()
4) $""

它们全部返回一个字符串,并且仅在缓存中创建一个值。

另一方面:

string+= string2;
string+= string2;
string+= string2;
string+= string2;

在缓存中创建4个不同的值,因为有4个“;”。

因此,编写如下所示的代码会更加容易,但是您将创建五个内插字符串,如CarlosMuñoz所纠正的那样:

string msg = $"Hello this is {myName}, " +
  $"My phone number {myPhone}, " +
  $"My email {myEmail}, " +
  $"My address {myAddress}, and " +
  $"My preference {myPreference}.";

这样您就可以很容易地读取代码,从而在缓存中创建一个字符串。我不确定性能,但是我确信MS会优化它(如果尚未执行的话)。


1
您的最后一个示例是错误的:实际上,您正在创建两个字符串:一个来自插值字符串,另一个来自其余字符串。请注意,只有带有{myName}的那个被插值,其他的没有按预期工作。
卡洛斯·穆尼奥斯

1
如果将$放在5个字符串的前面,则会创建5个内插字符串,每个内插字符串都有自己的字符串String.Format(),然后在运行时用串联String.Concat。因此最好不要将其分成多行
CarlosMuñoz18年

1
您是正确的@CarlosMuñoz,我已更正它。感谢您发现错误。
BoBoDev '18年

8

请注意,您还可以将两者结合起来,这很酷(尽管看起来有些奇怪):

// simple interpolated verbatim string
WriteLine($@"Path ""C:\Windows\{file}"" not found.");

5
如果只是你可以决定在其中键入的顺序$@或者@$。不幸的是,它只能是$@
Bauss

2
@Bauss这很有道理。@定义如何表示字符串文字。$是的快捷方式string.Format。将其视为$(@"");
marsze

这并不是的捷径string.Format。呈现一个解析为的值是很麻烦的string.Format,因此它在某种程度上是有道理的,但并不是全部。
Bauss

3
我只是说$基本上是一个隐式函数调用,而它@是文字的一部分,就像m十进制文字中的一样。这就是为什么只有一个逻辑顺序的原因。
marsze

2
我知道这主要是在这里进行演示$,但是为了最大程度地兼容而不是硬编码目录分隔符是'/'还是'\',并且还避免了不可避免的螺丝钉会在应有的地方引起双斜杠或斜杠丢失一直以来,我建议Path.Combine()在处理目录和文件时使用而不是使用字符串连接。
jrh

6

它比string.Format更方便,您也可以在此处使用intellisense。

在此处输入图片说明

这是我的测试方法:

[TestMethod]
public void StringMethodsTest_DollarSign()
{
    string name = "Forrest";
    string surname = "Gump";
    int year = 3; 
    string sDollarSign = $"My name is {name} {surname} and once I run more than {year} years."; 
    string expectedResult = "My name is Forrest Gump and once I run more than 3 years."; 
    Assert.AreEqual(expectedResult, sDollarSign);
}

6

它表示字符串插值。

因为它将在字符串评估上添加编译时间保护,所以它将为您提供保护。

您将再也不会遇到例外 string.Format("{0}{1}",secondParamIsMissing)


6

下面的示例突出显示了使用内插字符串的各种优点,string.Format()包括整洁性和可读性。它还表明,{}与调用任何其他函数参数一样,对其中的代码进行求值string.Format()

using System;

public class Example
{
   public static void Main()
   {
      var name = "Horace";
      var age = 34;
      // replaces {name} with the value of name, "Horace"
      var s1 = $"He asked, \"Is your name {name}?\", but didn't wait for a reply.";
      Console.WriteLine(s1);

      // as age is an integer, we can use ":D3" to denote that
      // it should have leading zeroes and be 3 characters long
      // see https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-pad-a-number-with-leading-zeros
      //
      // (age == 1 ? "" : "s") uses the ternary operator to 
      // decide the value used in the placeholder, the same 
      // as if it had been placed as an argument of string.Format
      //
      // finally, it shows that you can actually have quoted strings within strings
      // e.g. $"outer { "inner" } string"
      var s2 = $"{name} is {age:D3} year{(age == 1 ? "" : "s")} old.";
      Console.WriteLine(s2); 
   }
}
// The example displays the following output:
//       He asked, "Is your name Horace?", but didn't wait for a reply.
//       Horace is 034 years old.

6

$语法不错,但是有一个缺点。

如果您需要类似字符串模板的内容,则在类级别上将其声明为field ...应放在适当的位置。

然后,您必须在同一级别上声明变量……这确实不酷。

在这种情况下使用string.Format语法会更好

class Example1_StringFormat {
 string template = $"{0} - {1}";

 public string FormatExample1() {
   string some1 = "someone";
   return string.Format(template, some1, "inplacesomethingelse");
 }

 public string FormatExample2() {
   string some2 = "someoneelse";
   string thing2 = "somethingelse";
   return string.Format(template, some2, thing2);
 }
}

使用globals并不是真的可以,而且-也不适用于globals

 static class Example2_Format {
 //must have declaration in same scope
 static string some = "";
 static string thing = "";
 static string template = $"{some} - {thing}";

//This returns " - " and not "someone - something" as you would maybe 
//expect
 public static string FormatExample1() {
   some = "someone";
   thing = "something";
   return template;
 }

//This returns " - " and not "someoneelse- somethingelse" as you would 
//maybe expect
 public static string FormatExample2() {
   some = "someoneelse";
   thing = "somethingelse";
   return template;
 }
}

这个答案很重要,因为它指出插值发生在“调用” $ string时,而不是在声明它时。
dx_over_dt

1
您可以在类范围内声明插值变量的反模式是正确的,但是,如果这些变量已经属于类属性,则此模式效果很好。
dx_over_dt

@dx_over_dt你错了。内插字符串在声明时进行评估。这就是示例代码毫无意义的原因。它也不会编译。
NineBerry19年

@NineBerry您对插值字符串在声明时进行评估以及对Example_ $ Format无法编译以及对示例代码没有意义都很正确:)我已更正了示例以更好地解释它。
汤姆(Tom)


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.