Por que .NET criar novas substrings em vez de apontar em cadeias existentes?
-
23-08-2019 - |
Pergunta
A partir de um olhar breve com refletor, parece que a memória String.Substring()
aloca para cada substring. Estou correto que este é o caso? Eu pensei que não seria necessário uma vez que strings são imutáveis.
Meu objetivo subjacente era criar um método de extensão IEnumerable<string> Split(this String, Char)
que aloca nenhuma memória adicional.
Solução
Uma razão pela qual a maioria das línguas com cordas imutáveis ??criar novas substrings em vez de se referir em cadeias existentes é porque isso irá interferir com a coleta de lixo aquelas cordas mais tarde.
O que acontece se uma string é utilizado para a sua substring, mas então a seqüência maior se torna inacessível (exceto através da substring). A seqüência maior será incobrável, porque isso iria invalidar a substring. O que parecia uma boa maneira de poupar memória a curto prazo torna-se uma fuga de memória a longo prazo.
Outras dicas
não é possível sem picar ao redor dentro .net usando classes de corda. Você teria que passar em torno de referências para uma matriz que era mutável e garantir que ninguém asneira.
Net irá criar uma nova seqüência cada vez que você pedir para ele. Única exceção a isso é internado cordas que são criados pelo compilador (e pode ser feito por você), que são colocados na memória uma vez e, em seguida, os ponteiros são estabelecidos para a cadeia por razões de memória e desempenho.
Cada corda tem que ter é dados próprios de cordas, com a maneira que a classe String é implementado.
Você pode fazer a sua própria estrutura SubString que utiliza parte de uma string:
public struct SubString {
private string _str;
private int _offset, _len;
public SubString(string str, int offset, int len) {
_str = str;
_offset = offset;
_len = len;
}
public int Length { get { return _len; } }
public char this[int index] {
get {
if (index < 0 || index > len) throw new IndexOutOfRangeException();
return _str[_offset + index];
}
}
public void WriteToStringBuilder(StringBuilder s) {
s.Write(_str, _offset, _len);
}
public override string ToString() {
return _str.Substring(_offset, _len);
}
}
Você pode completá-lo com outros métodos como comparação que também é possível fazer sem extrair a string.
Porque strings são imutáveis ??em .NET, cada operação de cadeia que resulta em um novo objeto string irá alocar um novo bloco de memória para o conteúdo de string.
Em teoria, poderia ser possível reutilizar a memória quando se extrai um substring, mas que faria a coleta de lixo muito complicada: e se a string original é de lixo coletado? O que aconteceria com o substring que compartilha um pedaço dele?
É claro, nada impede que a equipe .NET BCL para alterar esse comportamento em versões futuras do .NET. Ele não teria qualquer impacto sobre o código existente.
Adicionando a tal ponto que strings são imutáveis, você deve ser que o trecho a seguir irá gerar várias instâncias de String na memória.
String s1 = "Hello", s2 = ", ", s3 = "World!";
String res = s1 + s2 + s3;
s1 + s2 => nova instância string (temp1)
temp1 + s3 => nova instância string (temp2)
res é uma referência para temp2. ??