Cadena literal de varias líneas en C #

1047

¿Hay una manera fácil de crear un literal de cadena multilínea en C #?

Esto es lo que tengo ahora:

string query = "SELECT foo, bar"
+ " FROM table"
+ " WHERE id = 42";

Sé que PHP tiene

<<<BLOCK

BLOCK;

¿C # tiene algo similar?

Chet
fuente
1
No hay saltos de línea en su ejemplo. ¿Los quieres?
weiqure
8
No. Solo quería varias líneas por razones de visibilidad / limpieza del código.
Chet
66
En ese caso, las cadenas literales contienen los saltos de línea. Puede usar @ "...". Reemplace (Environment.NewLine, "") si lo desea.
weiqure
99
Debe considerar enlazar el 42como parámetro, especialmente si proviene de la entrada del usuario, para evitar la inyección de SQL.
Jens Mühlenhoff
@ JensMühlenhoff, ¿cómo alguien inyectará una cadena codificada, que se define en el código?
Sr. Boy

Respuestas:

1590

Puede usar el @símbolo delante de a stringpara formar un literal de cadena literal :

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

También no tiene que escapar caracteres especiales cuando se utiliza este método, a excepción de las comillas dobles como se muestra en la respuesta de Jon Skeet.

John Rasch
fuente
43
De todos modos, es un literal de cadena: es un literal de cadena literal con el signo @.
Jon Skeet
95
si su cadena contiene comillas dobles ("), puede escapar de ellas así:" "(eso es dos caracteres de comillas dobles)
Muad'Dib
8
¿Hay alguna manera de hacer lo anterior sin crear nuevas líneas? Tengo un texto realmente largo que me gustaría ver envuelto en el IDE sin tener que usar el signo más ("hola" + "allí").
noelicus
2
@noelicus - en realidad no, pero se puede solucionar de que mediante el uso de la técnica de weiqure encima de:@"my string".Replace(Environment.NewLine, "")
John Rasch
8
@afsharm Puede usar $ @ "texto"
Patrick McDonald
566

Se llama literal literal de cadena en C #, y es solo una cuestión de poner @ antes del literal. Esto no solo permite varias líneas, sino que también desactiva el escape. Entonces, por ejemplo, puedes hacer:

string query = @"SELECT foo, bar
FROM table
WHERE name = 'a\b'";

Sin embargo, esto incluye los saltos de línea (usando cualquier salto de línea que su fuente los tenga) en la cadena. Para SQL, eso no solo es inofensivo, sino que probablemente mejore la legibilidad en cualquier lugar donde vea la cadena, sino que en otros lugares puede que no sea necesario, en cuyo caso no necesitará usar un literal de cadena literal de varias líneas para comenzar, o eliminarlos de la cadena resultante.

La única escapatoria es que si desea una comilla doble, debe agregar un símbolo extra de comillas dobles:

string quote = @"Jon said, ""This will work,"" - and it did!";
Jon Skeet
fuente
2
Esta respuesta es incorrecta; introduce nuevas líneas que OP no desea.
TamaMcGlinn
@TamaMcGlinn: Agregaré algo a la respuesta sobre eso; no estaba claro cuando el OP escribió la pregunta.
Jon Skeet
105

El problema con el uso del literal de cadena que encuentro es que puede hacer que su código se vea un poco " extraño " porque para no obtener espacios en la cadena en sí, debe alinearse completamente a la izquierda:

    var someString = @"The
quick
brown
fox...";

Yuck

Entonces, la solución que me gusta usar, que mantiene todo bien alineado con el resto de su código es:

var someString = String.Join(
    Environment.NewLine,
    "The",
    "quick",
    "brown",
    "fox...");

Y, por supuesto, si lo que desea dividir lógicamente las líneas de una instrucción SQL como eres y en realidad no necesita una nueva línea, siempre puede sustituir Environment.NewLinepor " ".

dav_i
fuente
2
Mucho más limpio, gracias. Además, String.Concat funciona de manera similar y no requiere un separador.
Seth
Gracias. Me gusta String.Join con el delimitador "" para SQL, ya que permite la sangría / coincidencia de pares y evita tener que agregar el espacio inicial.
Rob en TVSeries.com
77
Aunque fea, la primera versión no requiere ningún código para ejecutarse . La segunda opción obviamente tiene una sobrecarga en tiempo de ejecución para concatenar las cadenas separadas.
Ido codificación
@GoneCoding, ¿estás seguro de eso? El compilador puede optimizar la concatenación.
William Jockusch
@WilliamJockusch: en general, las llamadas a funciones que no son del operador (como unirse) se dejan intactas y no se optimizan. Mejor compruebe el código compilado, pero apostaría que esa llamada no está optimizada.
Gone Coding
104

Otra cosa que debes observar es el uso de literales de cadena en string.Format. En ese caso, debe escapar de llaves / llaves '{' y '}'.

// this would give a format exception
string.Format(@"<script> function test(x) 
      { return x * {0} } </script>", aMagicValue)
// this contrived example would work
string.Format(@"<script> function test(x) 
      {{ return x * {0} }} </script>", aMagicValue)
Martin Clarke
fuente
14
¿Y qué diferencia hace? Con o sin "@", debe duplicar "{{" para obtener "{" como carácter imprimible, es cuestión de String.Format, no de contenido de cadena.
greenoldman
12
Es un problema notable para las personas que desean poner el código Javascript en una cadena, lo que se puede hacer con mayor frecuencia en literales de cadena textuales que las cadenas normales.
Ed Brannin
2
En el nuevo C # 6.0, puede usar un operador de propiedad indexada junto con el literal de cadena literal (como este $ @ "El valor es {this.Value}";)
Heliac
2
@Heliac Creo que te refieres a que las cadenas interpoladas también pueden ser literales con esa sintaxis. var query = $ @ "select foo, barra de la tabla donde id = {id}";
brianary 01 de
102

Como nota al margen, con C # 6.0 ahora puede combinar cadenas interpoladas con el literal de cadena literal:

string camlCondition = $@"
<Where>
    <Contains>
        <FieldRef Name='Resource'/>
        <Value Type='Text'>{(string)parameter}</Value>
    </Contains>
</Where>";
Heliaco
fuente
10
Genial, no sabía sobre esto hasta ahora. Si alguien está interesado: el $ thingy se llama "Cadenas interpoladas" y puede leerlo en detalle aquí: msdn.microsoft.com/en-us/library/dn961160.aspx
Hauke ​​P.
tx,
reparó
1
Supongo que una llave literal debe doblarse, por ejemplo, $@"{{example literal text { fooString }. }}" esto puede confundir a algunos porque Angular, React y Vue.js usan la convención opuesta.
Patrick Szalapski
58

¿Por qué las personas siguen confundiendo cadenas con literales de cadena? La respuesta aceptada es una gran respuesta a una pregunta diferente; no a este

Sé que este es un tema antiguo, pero vine aquí posiblemente con la misma pregunta que el OP, y es frustrante ver cómo la gente sigue interpretándolo mal. O tal vez lo estoy leyendo mal, no lo sé.

En términos generales, una cadena es una región de la memoria de la computadora que, durante la ejecución de un programa, contiene una secuencia de bytes que pueden asignarse a caracteres de texto. Un literal de cadena, por otro lado, es una pieza de código fuente, aún no compilada, que representa el valor utilizado para inicializar una cadena más adelante, durante la ejecución del programa en el que aparece.

En C #, la declaración ...

 string query = "SELECT foo, bar"
 + " FROM table"
 + " WHERE id = 42";

... no produce una cadena de tres líneas sino un trazador de líneas; la concatenación de tres cadenas (cada una inicializada de un literal diferente) ninguna de las cuales contiene un modificador de nueva línea.

Lo que el OP parece estar preguntando, al menos lo que estaría preguntando con esas palabras, no es cómo introducir, en la cadena compilada, saltos de línea que imitan a los que se encuentran en el código fuente, sino cómo separarlos para mayor claridad. , una sola línea de texto en el código fuente sin introducir saltos en la cadena compilada. Y sin requerir un tiempo de ejecución prolongado, pasó uniéndose a las múltiples subcadenas que provienen del código fuente. Al igual que las barras invertidas finales dentro de un literal de cadena multilínea en javascript o C ++.

Sugerir el uso de cadenas literales, no importa StringBuilders , so String.Joinincluso funciones anidadas con reversiones de cadenas y qué no, me hace pensar que la gente realmente no comprende la pregunta. O tal vez no lo entiendo.

Hasta donde yo sé, C # no tiene (al menos en la versión paleolítica que todavía estoy usando, de la década anterior) una característica para producir limpiamente literales de cadena multilínea que se puedan resolver durante la compilación en lugar de la ejecución.

Tal vez las versiones actuales sí lo admiten, pero pensé que compartiría la diferencia que percibo entre cadenas y literales de cadena.

ACTUALIZAR:

(Del comentario de MeowCat2012) Puedes. El enfoque "+" de OP es el mejor. Según las especificaciones, la optimización está garantizada: http://stackoverflow.com/a/288802/9399618

Carvo Loco
fuente
1
La confusión que algunos (incluido yo mismo) pueden haber tenido al mirar la pregunta original es que el formato here-doc (shell, php, perl, ...) incluye las nuevas líneas. Entonces, si el OP se compara con el heredoc de PHP, incluir las nuevas líneas no debería haber sido un problema.
Tanktalus
Exactamente. Hasta donde sé, su respuesta "no, no puede hacer esto en C #" es correcta.
TamaMcGlinn
Me dijeron que en Java, una cadena concatenada se convertirá en una sola cadena literal en el código de bytes compilado. ¿Quizás dot Net lo haga también?
Meow Cat 2012
2
Usted puede. El enfoque "+" de OP es el mejor. Según las especificaciones, la optimización está garantizada: stackoverflow.com/a/288802/9399618 Aún así, eres uno de los pocos que entiende la pregunta. ¿Sería posible eliminar o doblar respuestas potencialmente engañosas pero aceptadas?
Meow Cat 2012
1
Gracias por la pista, @ MeowCat2012. Mirando un código descompilado, parece que ese es el caso.
Carvo Loco
12

No he visto esto, así que lo publicaré aquí (si está interesado en pasar una cadena, también puede hacerlo). La idea es que puede dividir la cadena en varias líneas y agregar su propio contenido (también en varias líneas) de la forma que desee. Aquí "tableName" se puede pasar a la cadena.

    private string createTableQuery = "";

    void createTable(string tableName)
    {

         createTableQuery = @"CREATE TABLE IF NOT EXISTS
                ["+ tableName  + @"] (
               [ID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 
               [Key] NVARCHAR(2048)  NULL, 
               [Value] VARCHAR(2048)  NULL
                                )";
    }
RobertHana
fuente
10
bastante peligroso diría: fácil para alguien hacer inyecciones sql de esa manera.
Kai
44
Está bien siempre que sepa que todas sus variables (¡si es que hay alguna!) En su cadena de consulta provienen de constantes de código u otra fuente segura; por ejemplo, createTable("MyTable"); en cualquier caso, la pregunta del OP fue sobre cómo ingresar literales de cadena multilínea directamente en el código , no cómo construir consultas de base de datos per se. :)
dbeachy1
2
probablemente debería usar un generador de cadenas, especialmente si el número de más (+) es mucho.
gldraphael
8

Puedes usar @ y "" .

        string sourse = @"{
        ""items"":[
        {
            ""itemId"":0,
            ""name"":""item0""
        },
        {
            ""itemId"":1,
            ""name"":""item1""
        }
        ]
    }";
vovkas
fuente
2
Debe explicar qué hace cada uno, ya que @ es para una cadena literal y "" es para escapar de comillas dobles.
AFract
4

Sí, puede dividir una cadena en varias líneas sin introducir nuevas líneas en la cadena real, pero no es bonito:

string s = $@"This string{
string.Empty} contains no newlines{
string.Empty} even though it is spread onto{
string.Empty} multiple lines.";

El truco consiste en introducir código que se evalúe como vacío, y ese código puede contener nuevas líneas sin afectar la salida. Adapte este enfoque de esta respuesta a una pregunta similar.

Aparentemente existe cierta confusión sobre cuál es la pregunta, pero hay dos pistas de que lo que queremos aquí es un literal de cadena que no contenga caracteres de nueva línea, cuya definición abarque varias líneas. (en los comentarios lo dice, y "esto es lo que tengo" muestra código que no crea una cadena con nuevas líneas)

Esta prueba unitaria muestra la intención:

    [TestMethod]
    public void StringLiteralDoesNotContainSpaces()
    {
        string query = "hi"
                     + "there";
        Assert.AreEqual("hithere", query);
    }

Cambie la definición anterior de consulta para que sea un literal de cadena, en lugar de la concatenación de dos literales de cadena que el compilador puede o no optimizar en uno.

El enfoque de C ++ consistiría en finalizar cada línea con una barra diagonal inversa, haciendo que el carácter de nueva línea se escape y no aparezca en la salida. Desafortunadamente, todavía existe el problema de que cada línea después de la primera debe dejarse alineada para no agregar espacios en blanco adicionales al resultado.

Solo hay una opción que no se basa en optimizaciones del compilador que podrían no suceder, que es poner su definición en una línea. Si desea confiar en las optimizaciones del compilador, el + que ya tiene es excelente; no tiene que alinear a la izquierda la cadena, no obtiene nuevas líneas en el resultado, y es solo una operación, sin llamadas de función, para esperar la optimización.

TamaMcGlinn
fuente
1
Creativo. Sin embargo, parece que (no confirmado) esto se trataría como una cadena de formato y podría o no optimizarse. Por otro lado, el enfoque "+" de OP es el mejor. Según las especificaciones, la optimización está garantizada: stackoverflow.com/a/288802/9399618
Meow Cat 2012
4

Agregue varias líneas: use @

string query = @"SELECT foo, bar
FROM table
WHERE id = 42";

Agregue valores de cadena al medio: use $

string text ="beer";
string query = $"SELECT foo {text} bar ";

Cadena de línea múltiple Agregue valores al medio: use $ @

string text ="Customer";
string query = $@"SELECT foo, bar
FROM {text}Table
WHERE id = 42";
Isanka Thalagala
fuente
3

Si no desea espacios / líneas nuevas, la adición de cadenas parece funcionar:

var myString = String.Format(
  "hello " + 
  "world" +
  " i am {0}" +
  " and I like {1}.",
  animalType,
  animalPreferenceType
);
// hello world i am a pony and I like other ponies.

Puede ejecutar lo anterior aquí si lo desea.

rattray
fuente
2
Un defecto (inadvertidamente demostrado) con este enfoque es que debes tener mucho cuidado para incluir espacios donde los desees. Recomendaría un enfoque más consistente de lo que tomé (por ejemplo, siempre al comienzo de la línea).
Rattray
Sin embargo, string + string es justo con lo que OP comenzó.
TamaMcGlinn
1

Sé que llego un poco tarde a la fiesta, pero quiero recomendar https://www.buildmystring.com/ para futuros lectores de este hilo. Puede convertir cualquier código a literal de cadena C # usando este sitio.

aadi1295
fuente
-10

Puede usar estos dos métodos:

    private static String ReverseString(String str)
    {
        int word_length = 0;
        String result = "";
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == ' ')
            {
                result = " " + result;
                word_length = 0;
            }
            else
            {
                result = result.Insert(word_length, str[i].ToString());
                word_length++;
            }
        }
        return result;
    }
//NASSIM LOUCHANI
    public static string SplitLineToMultiline(string input, int rowLength)
    {
        StringBuilder result = new StringBuilder();
        StringBuilder line = new StringBuilder();

        Stack<string> stack = new Stack<string>(ReverseString(input).Split(' '));

        while (stack.Count > 0)
        {
            var word = stack.Pop();
            if (word.Length > rowLength)
            {
                string head = word.Substring(0, rowLength);
                string tail = word.Substring(rowLength);

                word = head;
                stack.Push(tail);
            }

            if (line.Length + word.Length > rowLength)
            {
                result.AppendLine(line.ToString());
                line.Clear();
            }

            line.Append(word + " ");
        }

        result.Append(line);
        return result.ToString();
    }

En SplitLineToMultiline (), debe definir la cadena que desea usar y la longitud de la fila, es muy simple. Gracias .

nassimlouchani
fuente