Implementações de uma estrutura “Point3” em C#, como era em 2002, como é em 2019

Elemar Júnior

Voltemos no tempo. 2002!

using System;

namespace Foo
{
    class Program
    {
        static void Main(string[] args)
        {
            Point3F p = new Point3F(2, 3, 4);
            Console.WriteLine("The Point is " + p.ToString());
        }
    }

    public struct Point3F
    {
        public Point3F(float x, float y, float z)
        {
            _x = x;
            _y = y;
            _z = z;
        }
        
        private readonly float _x;
        public float X 
        {
            get 
            { 
                return _x; 
            }
        }

        private readonly float _y;
        public float Y 
        {
            get 
            {
                return _y;
            }
        }

        private readonly float _z;
        public float Z
        {
            get
            {
                return _z;
            }
        }

        public override string ToString() 
        {
            return string.Format("{0}; {1}; {2}", _x, _y, _z);
        }
    }
}

Agora, de volta para o presente. 2019!

using static System.Console;

namespace Foo
{
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Point3<float>(2, 3, 4);
            WriteLine($"The Point is {p}");
        }
    }

    public readonly struct Point3<T>
    {
        public Point3(T x, T y, T z) =>
            (X, Y, Z) = (x, y, z);
      
        public T X { get; }
        public T Y { get; }
        public T Z { get; }

        public override string ToString() => $"{X}, {Y}, {Z}";
    }
}

Não é difícil perceber, olhando o código do exemplo, que hoje em dia é possível escrever muito menos código, com muito mais expressividade.

As auto-properties, por exemplo, retiraram a necessidade de criármos campos específicos, manualmente, para armazenar seus valores.  Na prática, os campos ainda são criados pelo compilador e, a propóstito, como as propriedades são somente leitura, os campos gerados pelo compilador também são.

Os expression bodied members também reduziram consideravelmente a quantidade de “{}” espalhadas pelo código e tornaram a leitura do código mais rápida.

A possibilidade de declarar o uso de uma classe estática, com using static, para acesso direto a seus membros também torna o código mais limpo, embora, um pouco estranho a primeira vista..

As interpolações de string deram mais fluência para as contatenações.

Por fim, como novidade no C# 8,  agora structs genéricas, quando usadas com parâmetros de tipo sendo tipos primitivos, são valores não gerenciados (unmanaged).

Bônus

A utilização de generics com structs, sem prejuizo de performance para parâmetros de tipos primitivos foi um grande avanço. Entretanto, infelizmente, ainda há desafios de generalização que precisam ser superados.

No código que compartilhamos acima, se precisássemos, por exemplo, calcular o “comprimento do vetor”, da origem até o ponto especificado, não teríamos como escrever uma constraint que facilitasse a generalização para numéricos, permitindo o uso simples de funções como Sqrt. A explicação para isso é dada pelo próprio Anders.

And it’s not clear that the added complexity is worth the small yield that you get. If something you want to do is not directly supported in the constraint system, you can do it with a factory pattern. You could have a Matrix, for example, and in that Matrix you would like to define a dot product method. That of course that means you ultimately need to understand how to multiply two Ts, but you can’t say that as a constraint, at least not if T is int, double, or float. But what you could do is have your Matrix take as an argument a Calculator, and in Calculator, have a method called multiply. You go implement that and you pass it to the Matrix.

Consideramos essa, uma abordagem “inteligente demais”. No mundo real, código “inteligente demais” é difícil de manter.

#nullable enable

using System;
using System.Collections.Generic;

using static System.Console;
using static System.Math;

namespace Foo
{
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Point3<float>(3, 4, 0);
            WriteLine($"The Point is {p}. Length is {p.Length}");
        }
    }

    public readonly struct Point3<T>
    {
        private static readonly ILengthCalculator<T>? LengthCalculator = LengthCalculatorFactory.Of<T>();
        
        public Point3(T x, T y, T z) =>
            (X, Y, Z) = (x, y, z);
      
        public T X { get; }
        public T Y { get; }
        public T Z { get; }

        public override string ToString() => $"{X}, {Y}, {Z}";

        public double Length => LengthCalculator?.ComputeLength(X, Y, Z) ?? double.NaN;

    }

    public interface ILengthCalculator
    {
    };

    public interface ILengthCalculator<in T> : ILengthCalculator
    {
        double ComputeLength(T x, T y, T z);
    }

    public class LengthCalculatorForInt : ILengthCalculator<int>
    {
        public double ComputeLength(int x, int y, int z) => Sqrt(x * x + y * y + z * z);
    }

    public class LengthCalculatorForDouble : ILengthCalculator<double>
    {
        public double ComputeLength(double x, double y, double z) => Sqrt(x * x + y * y + z * z);
    }
    
    public class LengthCalculatorForFloat : ILengthCalculator<float>
    {
        public double ComputeLength(float x, float y, float z) => Sqrt(x * x + y * y + z * z);
    }
    
    public static class LengthCalculatorFactory
    {
        private static readonly Dictionary<Type, ILengthCalculator> Calculators
            = new Dictionary<Type, ILengthCalculator>()
            {
                {typeof(int), new LengthCalculatorForInt()},
                {typeof(float), new LengthCalculatorForFloat()},
                {typeof(double), new LengthCalculatorForDouble()},
            };
        
        public static ILengthCalculator<T>? Of<T>() 
            => (ILengthCalculator<T>) Calculators[typeof(T)];

    }
}

Se o desejo da Microsoft é adicionar suporte extendido para traits, por exemplo, a decisão de Anders, no passado, provavelmente terá de ser revista.

De qualquer forma, a inicialização do dicionário e o controle de propagação de nulls, mostradas no código, também foram grandes avanços da linguagem.

Em resumo

No passado
C# era uma linguagem poderosa mas substancialmente mais verbosa. Além disso, carecia de algumas verificações por parte do compilador quanto a segurança do código (quanto a propagação de nulidade)
No presente
C# ganhou contornos funcionais que a tornaram muito mais expressiva. Os códigos ficaram muito menores e fáceis de ler. Além disso, agora contamos com controle de propagação de nulidade que impede toda uma categoria de erros em tempo de execução..
Esperamos para o futuro
O sistema de contraints para tipos genéricos do C# ainda é muito limitado. Se a empresa pretende realmente investir em “traits” como recurso de modelagem, precisará revisitar esse posicionamento.

Compartilhe este insight:

Comentários

Participe deixando seu comentário sobre este artigo a seguir:

Subscribe
Notify of
guest
0 Comentários
Inline Feedbacks
View all comments

AUTOR

Elemar Júnior
Fundador e CEO da EximiaCo atua como tech trusted advisor ajudando empresas e profissionais a gerar mais resultados através da tecnologia.

NOVOS HORIZONTES PARA O SEU NEGÓCIO

Nosso time está preparado para superar junto com você grandes desafios tecnológicos.

Entre em contato e vamos juntos utilizar a tecnologia do jeito certo para gerar mais resultados.

Insights EximiaCo

Confira os conteúdos de negócios e tecnologia desenvolvidos pelos nossos consultores:

Arquitetura de Dados

Insights de um DBA na análise de um plano de execução

Especialista em performance de Bancos de Dados de larga escala
Arquitetura de Software

Estratégias para modernização do legado

Desenvolvedor .NET/NodeJs e especialista em Kafka com experiência em startups e grandes empresas
Infraestrutura e Nuvem

Migração para a nuvem, mais do que mudança tecnológica, implica em mudança da cultura organizacional

Engenheiro de nuvem, arquiteto de software e especialista em Containers e Devops

Acesse nossos canais

Simplificamos, potencializamos e aceleramos resultados usando a tecnologia do jeito certo

EximiaCo 2022 – Todos os direitos reservados

0
Queremos saber a sua opinião, deixe seu comentáriox
()
x

Implementações de uma estrutura “Point3” em C#, como era em 2002, como é em 2019

Para se candidatar nesta turma aberta, preencha o formulário a seguir:

Condição especial de pré-venda: R$ 14.000,00 - contratando a mentoria até até 31/01/2023 e R$ 15.000,00 - contratando a mentoria a partir de 01/02/2023, em até 12x com taxas.

Tenho interesse nessa capacitação

Para solicitar mais informações sobre essa capacitação para a sua empresa, preencha o formulário a seguir:

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

O seu insight foi excluído com sucesso!

O seu insight foi excluído e não está mais disponível.

O seu insight foi salvo com sucesso!

Ele está na fila de espera, aguardando ser revisado para ter sua publicação programada.

Tenho interesse em conversar

Se você está querendo gerar resultados através da tecnologia, preencha este formulário que um de nossos consultores entrará em contato com você:

Tenho interesse nessa solução

Se você está procurando este tipo de solução para o seu negócio, preencha este formulário que um de nossos consultores entrará em contato com você:

Tenho interesse neste serviço

Se você está procurando este tipo de solução para o seu negócio, preencha este formulário que um de nossos consultores entrará em contato com você:

× Precisa de ajuda?