TransWikia.com

Verificar dados antes de usar como elemento de array

Stack Overflow em Português Asked on January 4, 2022

No chat foi perguntado algo sobre validação de entrada de dados para evitar erro de pegar fora da faixa permitida. Achei a pergunta interessante e pena que não foi postada aqui, estou postando:

public static T[] ReplaceRange<T>(this T[]input, T[] replacement, int start) {
    for (int i = start; i < input.Length; i++) {
        T replacementItem = replacement[i - start];
        input[i] = replacementItem;
    }
    return input;
}

Como evitar o erro?

One Answer

Pontos de interesse:

  • Contratos
  • Erros de programação são resolvidos consertando-os
  • Algumas reorganizações
  • Existe coisa pronta que funciona melhor

Existem algumas formas, eu tenho gostado de usar contratos. Ele tem algumas desvantagens, mas em geral ajuda e deveria ser usado mais, o .NET usa o tempo todo em seus códigos. Se não gostar dele é só mudar para um if e lançar uma exceção (é o mesmo que o Requires() faz internamente na maioria dos casos, mas tem alguns que ele poderá ser retirado da execução, veja abaixo). Muda alguma coisa? Não muito, apenas que o erro é mais específico, e isso tem implicações positivas, mas pode mudar algo importante.

Uma outra possibilidade é usar um Assert() que pode ser garantidamente desligado, o que pode ser uma boa, afinal o erro Out of Bounds é erro de programação e a única solução possível é consertar o erro, a única coisa que podemos fazer é garantir que um código bem testado dê o erro em tempo de desenvolvimento e se tiver algum erro na chamada também tenha o problema resolvido. Se tiver certeza que o erro não acontece mais o Assert() não vai para produção, já o Requires() pode ir, mas ao mesmo tempo pode ser mais seguro se não tiver tanta certeza assim.

Na versão atual do Visual Studio essa retirada do Requires() não é automática, precisa instalar e configurar o uso de análise e retirada automática dos contratos.

Outra possibilidade é fazer nada, afinal erro de programação se resolve e não tenta contornar, e eu tenho optado por isso. Mas pode fazer algo assim:

Os nomes dos parâmetros do exemplo não são bons, são enganosos.

using static System.Console;
using static System.Diagnostics.Contracts.Contract;

public class Program {
    public static void Main() {
        foreach (var item in ReplaceRange(new int[] {0, 1, 2, 3, 4}, new int[] {5, 6, 7, 8, 9}, 2)) WriteLine(item);
    }
    public static T[] ReplaceRange<T>(T[] source, T[] destination, int start) {
        Requires(start >= 0, "O índice de início não pode ser menor que zero");
        Requires(start < source.Length, "O índice de início não pode ser maior que o fim do array");
        Requires(destination.Length >= source.Length - start, "O índice de início não pode ser maior que o que cabe");
        for (int i = start, j = 0; i < source.Length; i++, j++) destination[i] = source[j];
        return destination;
    }
}

Veja funcionando no ideone. E no .NET Fiddle. Também coloquei no GitHub para referência futura.

Não vou colocar para rodar online porque esses ambientes são limitados, a grande vantagem é quando a ferramenta ajuda, o Visual Studio por exemplo pode informar de problemas durante o desenvolvimento, ou tem ferramentas que geram testes para você baseado no contrato encontrado dentro do método (preciso até me atualizar porque mudou desde a última vez que usei).

Mas pode simplificar isso e usar o Array.Copy() que já existe no .NET. Se quiser só faça um wrap simples para considerar apenas o início da fonte e ele preencher sozinho o início do destino e o tamanho da cópia.

Postei mais com intuito de mostrar que tem alternativas, algumas que poucos conhecem, tem maneiras de resolver sem fazer nada no código e há maneiras de usar o que já tem pronto que será bem mais rápido porque fará uma cópia blittable, até onde dá.

Answered by Maniero on January 4, 2022

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP