Back-End

8 fev, 2018

Novidades do C# 7.2 – Parte 03: read-only structs e o tipo Span<T>

Publicidade

Este terceiro artigo da série sobre novidades do C# 7.2 conta com exemplos de uso de read-only structs e do tipo Span<T>.

Caso queira acompanhar ou até mesmo rever os dois primeiros artigos da série, acesse os links a seguir:

Read-only structs

Estrutura presente desde as primeiras versões da linguagem C#, structs agora podem também ser definidos como somente leitura. Para isso, será necessário empregar o modificador readonly na declaração deste tipo de construção, conforme demonstrado na implementação da classe Temperatura:

using System;

namespace ExemploReadonlyStruct
{
    public readonly struct Temperatura
    {
        public double Celsius { get; }
        public double Fahrenheit { get; }
        public double Kelvin { get; }

        public Temperatura(double tempCelsius)
        {
            Celsius = tempCelsius;
            Fahrenheit = Math.Round((tempCelsius * 9 / 5) + 32, 2);
            Kelvin = tempCelsius + 273.15;
        }
    }
}

É importante destacar que todas as propriedades e campos de um struct marcado com readonly também deverão ser declarados como somente leitura.

O tipo Span<T>

Uma das novas estruturas que integram o C# 7.2, o tipo Span<T> foi concebido com o intuito de simplificar o gerenciamento e a realização de operações de escrita em regiões contíguas de memória. Sua utilização é possível tanto com construções do próprio .NET Framework – muitas vezes arrays de tipos primitivos – quanto em ações que envolvam a manipulação de recursos não gerenciados.

Devido ao fato de estar declarado como um struct, o tipo Span<T> não causa impactos adicionais envolvendo alocações na seção de memória conhecida como heap (diferente de objetos convencionais). Para habilitar a sua utilização em um projeto, será necessário adicionar o package System.Memory, que atualmente (Janeiro/2018) ainda se encontra em modo Preview:

Na listagem a seguir é possível observar um exemplo de uso de Span<T>:

  • Um array (arrayMemory) será gerado e servirá de base posteriormente para a criação de uma variável do tipo Span<T> (arraySpan);
  • O método ExibirValores apresentará em tela os valores associados a arrayMemory (as alterações realizadas em arraySpan refletirão na referência original);
  • A operação Arredondar receberá um Span<T>, efetuando o arredondamento de valores associados a esta estrutura em duas casas decimais. Este método será acionado em duas ocasiões, recebendo a primeira, e depois a segunda metade do Span<T> original (estes dois conjuntos de valores foram gerados por meio de uma chamada à operação Slice);
  • Uma soma de todos os itens associados a arraySpan também será efetuada. É possível notar neste ponto, e em outros, o uso de for para navegação pelos diversos itens; isto acontece porque a instrução foreach não é compatível com o tipo Span<T>.
using System;

namespace ExemploSpan
{
    class Program
    {
        private static void ExibirValores(double[] valores)
        {
            Console.Write("{ ");

            for (int i = 0; i < valores.Length; i++)
            {
                if (i > 0)
                    Console.Write(", ");
                Console.Write(valores[i]);
            }

            Console.WriteLine(" }");
        }

        private static void Arredondar(Span<double> valores)
        {
            for (int i = 0; i < valores.Length; i++)
                valores[i] = Math.Round(valores[i], 2);
        }

        static void Main(string[] args)
        {
            double[] arrayMemory = new double[10]
                { 10.56768, 5.376936, 8.6, 7.2,
                  6.728, 7.7251, 10.59, 20.7769,
                  2.57874, 3.5 };
            var arraySpan = new Span<double>(arrayMemory);

            ExibirValores(arrayMemory);

            Arredondar(arraySpan.Slice(0, 5));
            ExibirValores(arrayMemory);

            Arredondar(arraySpan.Slice(5));
            ExibirValores(arrayMemory);

            // Não é possível utilizar foreach com Span<T>
            double total = 0;
            for (int i = 0; i < arraySpan.Length; i++)
                total += arraySpan[i];
            Console.WriteLine(quot;Total: {total}");

            Console.ReadKey();
        }
    }
}

Na próxima imagem está o resultado da execução deste último exemplo:

Referências