Razones detrás de la implementación no intuitiva de C # String.Split ()

11

En C # si quiero dividir un stringpor otro string, tengo que hacer algo así:

testString.Split(new string[] { "anotherString" }, StringSplitOptions.None);

A partir de la String.Splitdocumentación sobrecargada de MSDN podemos ver la implementación y por qué se debe realizar dicha llamada.

Viniendo de Python , es difícil para mí entender correctamente por qué se necesita dicha llamada. Quiero decir que podría usar Regex.Splitpara obtener una sintaxis similar a la implementación de Python, pero tendría que hacerlo a costa de un menor rendimiento (tiempo de configuración) para cualquier cosa simple .

Básicamente, mi pregunta es por qué demonios no podemos simplemente hacer:

testString.Split("anotherString");

Tenga en cuenta que no estoy sugiriendo ningún prototipo ni implementación. Entiendo por qué no pudo implementar la versión anterior teniendo en cuenta la API actual. Mi objetivo era entender por qué podría haberse creado una API de este tipo teniendo en cuenta el beneficio que aporta la sintaxis anterior. A partir de ahora, la flexibilidad parece ser el objetivo de la corriente String.Splitque tiene sentido, pero para ser sincero, realmente pensé que había algún tipo de ganancia de rendimiento en alguna parte. Creo que estaba equivocado.

Scharette
fuente
3
Yo también estaba pensando en esto. Mi especulación es que simplemente no pusieron mucho esfuerzo en diseñar esta API. Y si se dieron cuenta de su error, ya era demasiado tarde.
Eufórico
@Caleth ¿Puedes dar más detalles sobre esto? tal vez me equivoque, pero no veo lo ambiguo que tiene. ¿Por qué no puedo hacer testString.Split(",.;");y testString.Split(new Char [] {',', '.', ';',);que no son lo mismo?
scharette
@Eufórico Yo también lo pensé, pero eso sería muy extraño. Espero que alguien venga con una respuesta más lógica.
scharette
Puede iterar sobre una cadena como si fuera así, IEnumerable<char>por lo que el prototipo adicional que sugiere puede parecer ambiguo en ciertos casos (¿delimita por la cadena completa o delimita por cada uno de sus caracteres?) Solo una suposición.
John Wu
@JohnWu Tal vez sea algo personal, pero para el 99.9% de las ocurrencias de sintaxis testString.Split("anotherString");, estoy bastante seguro de decir que el comportamiento esperado era delimitarse en toda la cadena ( anotherStringen este caso).
scharette

Respuestas:

15

A veces es útil dividir en más de un char / string, por lo que la API le permite proporcionar una matriz, lo que le brinda la máxima flexibilidad. En el caso de chars, obtienes tanto la sintaxis como la flexibilidad, ya que el parámetro está marcado como paramspara que puedas escribir en Split('x')lugar de hacerlo Split(new[]{'x'}).

Entonces, ¿por qué no hay una opción similar para las cadenas, que le permite escribir Split("x")?

Esta es quizás una consecuencia desafortunada de cómo está diseñada la API. Inicialmente solo permitía dividirse en caracteres. La división en cadenas se agregó en 2.0, probablemente porque es más complejo de implementar. Pero no fue posible agregar String.Split(string)o String.Split(string[])sobrecargar, ya que esto haría que la expresión fuera testString.Split(null)ambigua y este código ya no se compilaría.

testString.Split(null) en realidad es un idioma bastante común, ya que divide la cadena en espacios en blanco, por lo que dicha rotura sería demasiado generalizada para ser aceptable.

El uso de un nullparámetro como un interruptor para un comportamiento especial generalmente se considera un mal diseño en estos días, por lo que creo que es justo decir que esta API es simplemente defectuosa.

Tampoco hay Split(string[], Int32)ninguno, probablemente por una razón similar: sería ambiguo Split(char[], Int32)si el primer parámetro es null. No son sobrecargas similares con los StringSplitOptionsparámetros, pero estos fueron añadidos al mismo tiempo en 2.0, así que no hay ambigüedad se introdujo en el código existente.

Nota

Para ser claros, esta es solo mi hipótesis, no sé el pensamiento real de los diseñadores de marcos .net.

JacquesB
fuente
1
Bueno, ¿es eso útil? Lo dudo. Y es solo un corte de API, no uno de ABI.
Deduplicador
2
@Deduplicator: Split (nulo) se divide en espacios en blanco, por lo que es probablemente uno de los casos de uso más comunes para dividir, a pesar de que es un mal diseño de API usar un nulo como este.
JacquesB
1
Creo que @Deduplicator quería decir que Split(null)es inútil si lo permites Split(""). Además del hecho de que permitiría una mejor manera de sintaxis, el último es más detallado de todos modos ...
scharette
1
@scharette: Claro, pero no es posible cambiar ahora, sin romper la compatibilidad con versiones anteriores.
JacquesB
1
una nota: con la vista previa actual de C # 8, al desactivar la nulabilidad de los tipos de base String.Split(null)ya no sería ambigua, por lo que podrían agregar la sobrecarga
BgrWorker
2

Al no ser el autor de los métodos, no sé por qué se eligió ese conjunto de sobrecargas. Sin embargo, hay dos cosas a tener en cuenta aquí:

  1. Si se está dividiendo en un solo carácter, entonces la public string[] Split(params char[] separatorversión) se puede usar así:

    var splitValues = testString.Split(',');

    como el char[]es un paramsparámetro.

  2. Puede agregar fácilmente su propio método de extensión aquí para lograr lo que desea:

    public static class StringExtensions
    {
        public static string[] Split(this string source, string separator)
            => source.Split(new string[] { separator }, StringSplitOptions.None);
    }
    

    y ahora testString.Split("anotherString");funcionará para ti.

David Arno
fuente
1
Gracias por la respuesta. Aunque su respuesta es útil y concisa, no puedo estar de acuerdo con usted. Especialmente el segundo punto. ¿No es una razón más para tenerlo incorporado? Todo lo que hace es dejar que la comunidad cree una versión diferente de un método que todos (o casi todos) esperan que se comporten de la misma manera.
scharette
No intente debatir por cierto, su punto es completamente válido. Solo trato de entender la razón detrás de esto. Lógicamente debe haber una razón histórica o de rendimiento ...
scharette
@scharette: La razón es hacer que el método sea lo más general posible. Tan preferible como encuentre la firma de su método elegido, no funcionará para múltiples delimitadores. La versión de Microsoft funcionará para múltiples delimitadores, así como para su único delimitador.
Robert Harvey
@RobertHarvey Bueno, ¿no serían posibles ambos? Digamos que el método de extensión en la respuesta anterior era parte de la Stringclase, ambos serían posibles. Me equivoco ?
scharette
Creo que te estás perdiendo el punto. Su sobrecarga solo permite un delimitador. La sobrecarga de Microsoft permite más de uno. No puede llamar a su sobrecarga varias veces y lograr el mismo resultado; Así no es como funciona esto.
Robert Harvey
1

Los diferentes idiomas tienen reglas algo diferentes para las conversiones implícitas y la sobrecarga, y .NET Framework está diseñado para ser utilizable con cualquiera de ellos. En el Option Strict Offdialecto de VB.NET, Stringse puede pasar un valor de tipo a una función que espera un Char[]comportamiento equivalente al de llamar ToCharArray()a la cadena.

Creo que lo más sensato hubiera sido tener nombres separados para Split(que acepta un solo Charo String) y SplitMulti(que aceptaría un Char[]o String[]), pero .NET a veces parece favorecer el uso de sobrecarga solo para elegir diferentes tipos de operaciones. Desafortunadamente, no conozco ninguna forma de usar String.Splitpara acomodar cualquier escenario de uso que requiera distinguir diferentes tipos de delimitadores que no sea dividir por separado en cada uno.

Otra omisión es una opción para preservar los delimitadores, ya sea incluyéndolos al final de la cadena anterior, o al comienzo de la siguiente cadena, o haciendo que los elementos de matriz impares sean delimitadores, mientras que los elementos pares son las cosas entre ellos.

Super gato
fuente
1
.NET a veces parece favorecer el uso de sobrecarga solo para elegir diferentes tipos de operaciones. Tan cierto ...
scharette