Co oznacza $ przed ciągiem?


250

Zamierzałem użyć ciągów dosłownych, ale przez pomyłkę wpisałem $zamiast @.

Ale kompilator nie dał mi żadnego błędu i został pomyślnie skompilowany.

Chcę wiedzieć, co to jest i co robi. Szukałem tego, ale nic nie znalazłem.

Nie jest to jednak ciąg dosłowny, ponieważ nie mogę napisać:

string str = $"text\";

Czy ktoś wie, co oznacza $ciąg przed w C #.

string str = $"text";

Używam CTP programu Visual Studio 2015.

Odpowiedzi:


418

$jest skrótem od String.Formati jest używany z interpolacjami ciągów, co jest nową cechą C # 6. W twoim przypadku nie robi on nic, podobnie jak string.Format()nic.

Sprawdza się, gdy jest używany do budowania ciągów w odniesieniu do innych wartości. Co wcześniej trzeba było napisać jako:

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

Teraz staje się:

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

Istnieje również alternatywna - mniej znana - forma interpolacji łańcuchów znaków $@ (ważna jest kolejność dwóch symboli). Pozwala @""mieszać cechy łańcucha w $""celu obsługi interpolacji łańcucha bez konieczności korzystania z niego w \\całym łańcuchu. Więc następujące dwie linie:

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

wyświetli:

c:\a\b\c

29
Zauważ, że tak naprawdę nie używa String.Format, ale jest funkcją opartą na kompilatorze, a nie środowiskiem uruchomieniowym.
Shahar Prish,

2
Drobna uwaga, której się dzisiaj nauczyłem, jeśli używasz $@, to musisz uciekać od "postaci, używając "". Nie dotyczy to tylko korzystania $.
Flater

3
@ Flater To nie ma nic wspólnego z symbolem $. To samo zachowanie, jakie istniało przed pojawieniem się symbolu $.
BVernon,

2
Jeśli chodzi o wasze znaczenie kolejności symboli dosłownych (@) i interpolacji ($), jest to poprawione w C # 8, aby kolejność nie miała już znaczenia. Zobacz: devsanon.com/uncategorized/…
elkaz

38

Tworzy interpolowany ciąg .

Z MSDN

Służy do konstruowania ciągów. Interpolowane wyrażenie ciągu wygląda jak ciąg szablonu zawierający wyrażenia. Interpolowane wyrażenie ciągu tworzy ciąg, zastępując zawarte wyrażenia reprezentacjami ToString wyników wyrażeń.

np .:

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

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

W interpolowanym ciągu można używać wyrażeń

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

Zaletą jest to, że nie musisz martwić się kolejnością parametrów, jak to robisz String.Format.

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

Teraz, jeśli chcesz usunąć niektóre parametry, musisz przejść i zaktualizować wszystkie liczby, co już nie ma miejsca.

Pamiętaj, że stare dobre string.formatma nadal znaczenie, jeśli chcesz podać informacje o kulturze w swoim formatowaniu .


Zauważ, że prawdopodobnie nadal możesz użyć $i podać informacje o kulturze, jeśli przekonwertujesz dane na ciąg znaków w $wyrażeniu, używając poprawnej kultury, np {somevar.ToString(...,[Insert culture info here])}.
jrh

18

Przykładowy kod

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}");

Wynik

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

To interpolowane ciągi znaków . Możesz użyć interpolowanego łańcucha w dowolnym miejscu, w którym możesz użyć literału łańcucha. Podczas uruchamiania programu kod wykonywany byłby z interpolowanym literałem łańcuchowym, kod oblicza nowy łańcuch literalny, oceniając wyrażenia interpolacyjne. To obliczenie zachodzi za każdym razem, gdy wykonywany jest kod z interpolowanym łańcuchem.

Poniższy przykład tworzy wartość ciągu, w której obliczono wszystkie wartości interpolacji ciągu. Jest to wynik końcowy i ma ciąg typu. Wszystkie wystąpienia podwójnych nawiasów klamrowych (“{{“ and “}}”)są konwertowane na jeden nawias klamrowy.

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

Po wykonaniu powyżej 2 wierszy zmienna messagezawiera „Hello, World”.

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

Odniesienie - MSDN


10

Fajna funkcja. Chciałbym tylko podkreślić nacisk na to, dlaczego jest to lepsze niż string.format, jeśli nie jest to oczywiste dla niektórych osób.

Czytam kogoś mówiącego polecenie string.format na „{0} {1} {2}”, aby dopasować parametry. Nie musisz zamawiać „{0} {1} {2}” w string.format, możesz także zrobić „{2} {0} {1}”. Jeśli jednak masz wiele parametrów, na przykład 20, naprawdę chcesz zsekwencjonować ciąg znaków do „{0} {1} {2} ... {19}”. Jeśli jest to potargany bałagan, trudno będzie ci wyrównać parametry.

Za pomocą $ możesz dodać parametr inline bez zliczania parametrów. Dzięki temu kod jest znacznie łatwiejszy do odczytania i utrzymania.

Minusem $ jest to, że nie można łatwo powtórzyć parametru w ciągu, należy go wpisać. Na przykład, jeśli masz dość pisania System.Environment.NewLine, możesz zrobić string.format („... {0} ... {0} ... {0}”, System.Environment.NewLine), ale w $ musisz to powtórzyć. Nie możesz zrobić $ „{0}” i przekazać go do string.format, ponieważ $ „{0}” zwraca „0”.

Na marginesie przeczytałem komentarz w innym zduplikowanym tpoiku. Nie mogłem komentować, więc oto jest. On to powiedział

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

tworzy więcej niż jeden ciąg znaków. To nie jest prawda. Jeśli zrobisz to w jednym wierszu, utworzy tylko jeden ciąg i zostanie umieszczony w pamięci podręcznej łańcucha.

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

Wszystkie zwracają ciąg znaków i tworzą tylko jedną wartość w pamięci podręcznej.

Z drugiej strony:

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

Tworzy 4 różne wartości w pamięci podręcznej, ponieważ są 4 „;”.

W ten sposób łatwiej będzie napisać kod jak poniżej, ale utworzyłbyś pięć interpolowanych ciągów znaków, jak poprawił Carlos Muñoz:

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

Tworzy to jeden ciąg znaków w pamięci podręcznej, a kod jest bardzo łatwy do odczytania. Nie jestem pewien co do wydajności, ale jestem pewien, że stwardnienie rozsiane zoptymalizuje to, jeśli już tego nie robi.


1
Twój ostatni przykład jest zły: w rzeczywistości kratujesz dwa ciągi: jeden z interpolowanego ciągu, a drugi z pozostałych ciągów. Zauważ, że interpolowany jest tylko ten z {myName}, pozostałe nie działają zgodnie z oczekiwaniami.
Carlos Muñoz

1
A jeśli dodasz $ do 5 łańcuchów, wówczas utworzy 5 interpolowanych łańcuchów, każdy z własnymi, String.Format()a następnie połączonych w czasie wykonywania z String.Concat. Lepiej więc nie dzielić go na wiele linii
Carlos Muñoz

1
Masz rację @Carlos Muñoz, poprawiłem to. Dzięki za złapanie błędu.
BoBoDev,

8

Pamiętaj, że możesz również połączyć te dwa, co jest całkiem fajne (choć wygląda nieco dziwnie):

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

5
Jeśli tylko możesz zdecydować, w jakiej kolejności wpisałeś $@lub @$. Niestety może być tylko$@
Bauss

2
@Bauss Ma to sens. @określa sposób reprezentowania literału łańcucha. $jest skrótem do string.Format. Pomyśl o tym jak$(@"");
marsze

To nie jest tak naprawdę skrót string.Format. Jest cukierkowy do prezentowania wartości, która zostaje przeanalizowana do string.Format, więc częściowo ma sens, ale nie do końca.
Bauss

3
Mówię tylko, że $jest to w zasadzie implikowane wywołanie funkcji, podczas gdy @jest częścią literału, tak jak mw literale dziesiętnym. Dlatego istnieje tylko jedna logiczna kolejność.
marsze

2
Wiem, że było to głównie tutaj, aby zademonstrować $, ale ze względu na maksymalną kompatybilność i nie kodowanie na stałe, czy separatorem katalogów jest „/” czy „\”, a także unikanie nieuchronnego pomyłki powodującej podwójne cięcie lub brakujące cięcie tam, gdzie powinno być były jednym, zalecam używanie Path.Combine()zamiast łączenia łańcuchów podczas pracy z katalogami i plikami.
jrh

6

Jest to wygodniejsze niż string. Sformatuj i możesz tutaj również użyć intellisense.

wprowadź opis zdjęcia tutaj

A oto moja metoda testowa:

[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

Oznacza interpolację łańcuchów.

Będzie cię chronić, ponieważ dodaje ochronę czasu kompilacji do oceny ciągu.

Nie będziesz już otrzymywać wyjątku od string.Format("{0}{1}",secondParamIsMissing)


6

Poniższy przykład przedstawia różne zalety stosowania interpolowanych ciągów w string.Format()zakresie czystości i czytelności. Pokazuje również, że kod wewnątrz {}jest oceniany jak każdy inny argument funkcji, tak jak gdyby string.Format()był wywoływany.

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

Składnia $ jest dobra, ale z jednym minusem.

Jeśli potrzebujesz czegoś w rodzaju szablonu łańcucha, który jest deklarowany na poziomie klasy jako pole ... w jednym miejscu, tak jak powinno być.

Następnie musisz zadeklarować zmienne na tym samym poziomie ... co nie jest naprawdę fajne.

O wiele fajniej jest używać składni string.Format do tego typu rzeczy

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);
 }
}

Użycie globali nie jest w rzeczywistości w porządku, a poza tym - nie działa również z globalsami

 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;
 }
}

Ta odpowiedź jest ważna, ponieważ wskazuje, że interpolacja występuje, gdy „wywołujesz” ciąg $, a nie kiedy go deklarujesz.
dx_over_dt

1
Masz rację co do anty-wzorca deklarowania zmiennych interpolacyjnych w zakresie klasy, jednak jeśli zmienne te już należą do właściwości klasy, ten wzorzec działa dobrze.
dx_over_dt

@dx_over_dt Mylisz się. Interpolowane ciągi są oceniane w momencie ich deklaracji. Dlatego przykładowy kod nie ma sensu. Nie będzie się także kompilować.
NineBerry

@NineBerry Masz rację zarówno jeśli chodzi o interpolowane ciągi, które są oceniane w momencie ich deklaracji, jak i o Przykładowy Format $ nie kompiluje się, a przykładowy kod nie ma sensu :) Poprawiłem próbkę, aby lepiej to wyjaśnić.
Tom


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.