Motivos por trás da implementação não intuitiva do C # String.Split ()


11

Em C #, se eu quiser dividir um stringpor outro string, tenho que fazer algo assim:

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

Na String.Splitdocumentação sobrecarregada do MSDN, podemos ver a implementação e por que essa ligação deve ser feita.

Vindo do Python , é difícil para mim entender corretamente por que essa ligação é necessária. Quero dizer, eu poderia usar Regex.Splitpara obter uma sintaxe semelhante à implementação do Python, mas precisaria fazê-lo com o custo de menos desempenho (tempo de configuração) para algo simples .

Então, basicamente, minha pergunta é por que diabos não podemos simplesmente fazer:

testString.Split("anotherString");

Observe que não estou sugerindo nenhum protótipo nem implementação. Entendo por que você não conseguiu implementar a versão acima, considerando a API atual. Meu objetivo era entender por que essa API poderia ter sido criada considerando o benefício que a sintaxe acima traz. A partir de agora, a flexibilidade parece ser o objetivo da corrente String.Splitque faz sentido, mas, para ser sincero, realmente pensei que havia algum tipo de ganho de desempenho em algum lugar. Eu acho que estava errado.


3
Eu também estava pensando nisso. Minha especulação é que eles simplesmente não se esforçaram muito para projetar essa API. E se eles perceberam seu erro, já era tarde demais.
Euphoric

Caleth @ Você pode elaborar sobre isso. talvez eu esteja errado, mas não vejo o que há de ambíguo nisso. Por que não posso fazer testString.Split(",.;");e testString.Split(new Char [] {',', '.', ';',);quais não são a mesma coisa?
scharette

@Euphoric eu também pensei, mas isso seria tão estranho. Espero que alguém venha com uma resposta mais lógica.
Scharette

Você pode iterar sobre uma string como uma, IEnumerable<char>para que o protótipo adicional que você está sugerindo possa parecer ambíguo em certos casos (você delimita a string inteira ou delimita cada um de seus caracteres?) Apenas um palpite.
John Wu

@ JohnWu Talvez seja uma coisa pessoal, mas para 99,9% das ocorrências de sintaxe testString.Split("anotherString");, estou bastante confiante em dizer que o comportamento esperado era delimitar toda a string ( anotherStringneste caso).
Scharette

Respostas:


15

Às vezes, a divisão em mais de um caractere / sequência de caracteres é útil; portanto, a API permite que você forneça uma matriz, oferecendo a máxima flexibilidade. No caso de chars, você obtém simplicidade de sintaxe e flexibilidade, já que o parâmetro está marcado como paramspara que você possa escrever em Split('x')vez de Split(new[]{'x'}).

Então, por que não existe uma opção semelhante para strings, permitindo que você escreva Split("x")?

Essa talvez seja uma consequência infeliz de como a API é projetada. Inicialmente, ele só permitia dividir os caracteres. A divisão em strings foi adicionada na 2.0, provavelmente porque é mais complexa de implementar. Mas não foi possível adicionar String.Split(string)ou String.Split(string[])sobrecarregar, pois isso tornaria a expressão testString.Split(null)ambígua e esse código não seria mais compilado.

testString.Split(null) é, na verdade, um idioma bastante comum, pois divide a cadeia de caracteres em espaço em branco; portanto, essa quebra seria muito ampla para ser aceitável.

O uso de um nullparâmetro como uma opção para comportamentos especiais geralmente é considerado um design ruim hoje em dia, por isso acho justo dizer que essa API é falha.

Também não existe Split(string[], Int32), provavelmente por uma razão semelhante - seria ambíguo Split(char[], Int32)se o primeiro parâmetro fosse null. Não são sobrecargas semelhantes com os StringSplitOptionsparâmetros, mas estes foram todos adicionados ao mesmo tempo em 2,0, então há ambiguidade foi introduzido no código existente.

Nota

Para deixar claro, esta é apenas minha hipótese, não conheço o pensamento real dos designers de estrutura .net.


1
Bem, isso é útil? Duvido disso. E é apenas uma quebra de API, não uma ABI.
Deduplicator

2
@Duplicator: Split (null) divide em espaço em branco, portanto, é provavelmente um dos casos de uso mais comuns para split, mesmo que seja um mau design da API usar um nulo como este.
JacquesB

1
Eu acho que o @Deduplicator queria dizer que Split(null)é inútil se você permitir Split(""). Além do fato de que ele iria permitir uma melhor forma de sintaxe, o último é mais detalhado de qualquer maneira ...
scharette

1
@charette: Claro, mas não é possível mudar agora, sem interromper a compatibilidade com versões anteriores.
JacquesB

1
uma nota: com o atual C # 8 de visualização, desligando tipos base de nulidade String.Split(null)não seria mais ambígua, para que eles pudessem adicionar a sobrecarga
BgrWorker

2

Não sendo o autor dos métodos, não sei por que esse conjunto de sobrecargas foi escolhido. No entanto, há duas coisas a serem observadas aqui:

  1. Se você estiver dividindo em um único caractere, a public string[] Split(params char[] separatorversão) poderá ser usada assim:

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

    como o char[]é um paramsparâmetro

  2. Você pode adicionar facilmente seu próprio método de extensão aqui para obter o que deseja:

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

    e agora testString.Split("anotherString");funcionará para você.


1
Obrigado pelo feedback. Embora sua resposta seja útil e concisa, não posso concordar com você. Especialmente o segundo ponto. Não há mais um motivo para incorporá-lo? Tudo o que faz é deixar a comunidade criar uma versão diferente de um método que todos (ou quase todos) esperam que se comportem da mesma maneira.
Scharette

Não tentando debater a propósito, seu argumento é totalmente válido. Apenas tentando entender a razão por trás disso. Logicamente, deve haver uma razão histórica ou desempenho ...
scharette

@charette: O motivo é tornar o método o mais genérico possível. Por mais preferível que você encontre a assinatura do método escolhido, ela não funcionará para vários delimitadores. A versão da Microsoft funcionará para vários delimitadores, bem como para o seu delimitador único.
Robert Harvey

@RobertHarvey Bem, ambos não seriam possíveis? Digamos que o método de extensão na resposta acima fazia parte da Stringclasse, ambos seriam possíveis. Estou errado ?
Scharette

Eu acho que você está perdendo o ponto. Sua sobrecarga permite apenas um delimitador. A sobrecarga da Microsoft permite mais de um. Você não pode chamar sua sobrecarga várias vezes e obter o mesmo resultado; não é assim que isso funciona.
Robert Harvey

1

Idiomas diferentes têm regras um pouco diferentes para conversões implícitas e sobrecargas, e o .NET Framework foi projetado para ser usado com qualquer um deles. No Option Strict Offdialeto do VB.NET, um valor do tipo Stringpode ser passado para uma função que espera um Char[]comportamento equivalente a chamar ToCharArray()a string.

Eu acho que a coisa mais sensata a fazer seria ter nomes separados para Split(que aceita um único Charou String) e SplitMulti(que aceitaria um Char[]ou String[]), mas o .NET às vezes parece preferir usar a sobrecarga sozinho para escolher diferentes tipos de operações. Infelizmente, não conheço nenhuma maneira de usar String.Splitpara acomodar quaisquer cenários de uso que exijam a distinção de diferentes tipos de delimitadores que não sejam divididos separadamente em cada um.

Outra omissão é uma opção para preservar delimitadores, incluindo-os no final da sequência anterior ou no início da sequência seguinte, ou com elementos de matriz com números ímpares serem delimitadores, enquanto elementos com números pares são o que há entre eles.


1
Às vezes, o .NET parece preferir usar sobrecarga sozinho para escolher diferentes tipos de operações. Então é verdade ...
scharette
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.