TL; DR - Estou tentando projetar uma estrutura de dados ideal para definir unidades dentro de uma unidade de medida.
A Unit of measure
é essencialmente uma value
(ou quantidade) associada a a unit
. As unidades SI possuem sete bases ou dimensões. A saber: comprimento, massa, tempo, corrente elétrica, temperatura, quantidade de substância (moles) e intensidade luminosa.
Isso seria bastante direto, mas há várias unidades derivadas e taxas que usamos com frequência. Um exemplo de unidade combinada seria o Newton: kg * m / s^2
e uma taxa de exemplo seria tons / hr
.
Temos um aplicativo que depende muito de unidades implícitas. Incorporaremos as unidades no nome da variável ou coluna. Mas isso cria problemas quando precisamos especificar uma unidade de medida com unidades diferentes. Sim, podemos converter os valores na entrada e na exibição, mas isso gera muitos códigos gerais que gostaríamos de encapsular dentro de sua própria classe.
Existem várias soluções no codeplex e em outros ambientes colaborativos. O licenciamento para os projetos é agradável, mas o próprio projeto geralmente acaba sendo muito leve ou muito pesado. Estamos perseguindo nosso próprio unicórnio de "exatamente certo".
Idealmente, eu poderia definir uma nova unidade de medida usando algo como isto:
UOM myUom1 = nova UOM (10 volts);
UOM myUom2 = nova UOM (43,2, Newtons);
Obviamente, usamos uma mistura de unidades imperiais e SI com base nas necessidades de nossos clientes.
Também precisamos manter essa estrutura de unidades sincronizada com uma tabela futura do banco de dados para que também possamos fornecer o mesmo grau de consistência em nossos dados.
Qual é a melhor maneira de definir as unidades, unidades derivadas e taxas que precisamos usar para criar nossa classe de unidade de medida? Eu pude ver usando uma ou mais enumerações, mas isso pode ser frustrante para outros desenvolvedores. Uma única enumeração seria enorme com mais de 200 entradas, enquanto várias enumerações poderiam ser confusas com base nas unidades SI vs Imperial e em detalhes adicionais com base na categorização da própria unidade.
Exemplos enum mostrando algumas das minhas preocupações:
myUnits.Volt
myUnits.Newton
myUnits.meterSIUnit.meter
ImpUnit.foot DrvdUnit.Newton
DrvdUnitSI.Newton
DrvdUnitImp.FtLbs
Nosso conjunto de unidades em uso é muito bem definido e é um espaço finito. Precisamos da capacidade de expandir e adicionar novas unidades ou taxas derivadas quando houver demanda dos clientes por elas. O projeto está em C #, embora eu ache que os aspectos mais amplos do design sejam aplicáveis a vários idiomas.
Uma das bibliotecas que eu analisei permite a entrada de unidades de forma livre via string. A classe UOM deles analisou a sequência e colocou as coisas de acordo. O desafio dessa abordagem é que ela força o desenvolvedor a pensar e lembrar quais são os formatos corretos de string. E corro o risco de um erro / exceção de tempo de execução, se não adicionarmos verificações adicionais no código para validar as seqüências que estão sendo passadas no construtor.
Outra biblioteca criou essencialmente muitas classes com as quais o desenvolvedor teria que trabalhar. Juntamente com uma UOM equivalente forneceu um DerivedUnit
e RateUnit
e assim por diante. Essencialmente, o código era excessivamente complexo para os problemas que estamos resolvendo. Essa biblioteca permitiria essencialmente qualquer: qualquer combinação (o que é legítimo no mundo das unidades), mas estamos felizes em analisar nosso problema (simplificar nosso código), não permitindo todas as combinações possíveis.
Outras bibliotecas eram ridiculamente simples e nem sequer consideravam a sobrecarga do operador, por exemplo.
Além disso, não estou tão preocupado com tentativas de conversões incorretas (por exemplo: volts em metros). Os desenvolvedores são os únicos que acessarão neste nível neste momento e não precisamos necessariamente proteger contra esses tipos de erros.
Respostas:
As bibliotecas Boost para C ++ incluem um artigo sobre análise dimensional que apresenta uma implementação de exemplo de unidades de medida de manipulação.
Para resumir: As unidades de medida são representadas como vetores, com cada elemento do vetor representando uma dimensão fundamental:
Unidades derivadas são combinações delas. Por exemplo, força (massa * distância / tempo ^ 2) seria representada como
As unidades imperiais versus unidades SI podem ser tratadas adicionando um fator de conversão.
Essa implementação depende de técnicas específicas do C ++ (usando a metaprogramação de modelos para transformar facilmente diferentes unidades de medida em diferentes tipos de tempo de compilação), mas os conceitos devem ser transferidos para outras linguagens de programação.
fonte
mpl::vector_c<int,1,0,0,0,0,0,0>
) em vez de consts; o artigo apresenta a abordagem de consts primeiro por meio de explicação (e eu provavelmente não expliquei isso muito bem). Usar consts funcionaria como uma alternativa (você perderia alguma segurança do tipo em tempo de compilação). Usar um espaço para nome para evitar a poluição de nomes é certamente uma opção.Acabei de lançar o Units.NET no Github e no NuGet .
Ele fornece todas as unidades e conversões comuns. É leve, testado em unidade e suporta PCL.
Para sua pergunta:
Ainda estou para ver o Santo Graal das soluções neste domínio. Como você declara, pode facilmente se tornar muito complexo ou muito detalhado para trabalhar. Às vezes, é melhor manter as coisas simples e, para minhas necessidades, essa abordagem se mostrou suficiente.
Conversão explícita
Conversão dinâmica
fonte
Truple<T1, T2, T3>(x, y, z)
Tuple
. Não consigo ver suaUnitConverter
turma, mas parece que o IMO pode compartilhar uma funcionalidade semelhante à daTuple
turma.Se você pode alternar para F # em vez de usar C #, o F # possui um sistema de unidades de medida (implementado usando metadados nos valores) que parece se encaixar no que você está tentando fazer:
http://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure
Notavelmente:
fonte
Important: Units of measure look like a data type, but they aren't. .NET's type system does not support the behaviors that units of measure have, such as being able to square, divide, or raise datatypes to powers. This functionality is provided by the F# static type checker at compile time, **but units are erased from compiled code**. Consequently, it is not possible to determine value's unit at runtime.
Com base no fato de que todas as conversões necessárias são dimensionadas (exceto se você tiver que suportar conversões de temperatura. Os cálculos em que a conversão envolve um deslocamento são significativamente mais complexos), eu projetaria meu sistema de 'unidade de medida' da seguinte forma:
Uma classe que
unit
contém um fator de escala, uma sequência para a representação textual da unidade e uma referência à qual aunit
escala é dimensionada. A representação textual existe para fins de exibição e a referência à unidade base para saber em qual unidade está o resultado ao fazer cálculos em valores com unidades diferentes.Para cada unidade suportada,
unit
é fornecida uma instância estática da classe.Uma classe que
UOM
contém um valor e uma referência aos valoresunit
. AUOM
classe fornece operadores sobrecarregados para adicionar / subtrair outroUOM
e para multiplicar / dividir com um valor adimensional.Se a adição / subtração for realizada em duas
UOM
com o mesmounit
, é realizada diretamente. Caso contrário, ambos os valores são convertidos em suas respectivas unidades base e os adicionados / subtraídos. O resultado é relatado como estando na baseunit
.O uso seria como
Como as operações em unidades incompatíveis não são consideradas um problema, não tentei tornar o tipo de design seguro nesse aspecto. É possível adicionar uma verificação de tempo de execução, verificando se duas unidades se referem à mesma unidade base.
fonte
95F - 85F
? O que é20C - 15C
? Nos dois exemplos, ambosUOM
s teriam o mesmounit
. As subtrações seriam executadas diretamente?10 F
e5 C
. Os cálculos são realizados diretamente, se possível, para evitar conversões desnecessárias. Seria bastante trivial adicionar métodos de conversão de unidadesUOM
, mas para a conversão Celsius-Fahrenheit, aunit
classe teria que ser estendida com a possibilidade de um deslocamento além de um fator de escala.95F - 85F
! =10F
.95F
por85F
? Que eu saiba, Fahrenheit ainda é uma escala linear.20C - 15C = 5C
, estamos dizendo293.15K - 288.15K = 278.15K
, o que está claramente errado.Pense no que seu código está fazendo e no que ele permitirá. Ter uma enumeração simples com todas as unidades possíveis permite fazer algo como converter Volts em metros. Obviamente, isso não é válido para um ser humano, mas o software tentará com prazer.
Eu fiz algo vagamente semelhante a isso uma vez, e minha implementação teve classes básicas abstratas (comprimento, peso, etc.) que foram implementadas
IUnitOfMeasure
. Cada classe de bases abstratas definia um tipo padrão (a classeLength
tinha uma implementação padrão da classeMeter
) que seria usada para todo o trabalho de conversão. Portanto,IUnitOfMeasure
implementou dois métodos diferentes,ToDefault(decimal)
eFromDefault(decimal)
.O número real que eu queria agrupar era um tipo genérico que aceitava
IUnitOfMeasure
como argumento genérico. Dizer algo comoMeasurement<Meter>(2.0)
fornece segurança de tipo automático. A implementação das conversões implícitas e dos métodos matemáticos adequados nessas classes permite que você faça coisas comoMeasurement<Meter>(2.0) * Measurement<Inch>(12)
e retorne um resultado no tipo padrão (Meter
). Eu nunca trabalhei com unidades derivadas como Newtons; Eu simplesmente os deixei como Quilograma * Medidor / Segundo / Segundo.fonte
Acredito que a resposta esteja na resposta Stack Overflow do MarioVW para:
Eu tinha uma necessidade semelhante para o meu aplicativo.
Tuple
também é imutável, o que também é válido para objetos como Pesos e Medidas ... Como diz o ditado "um litro a libra ao redor do mundo".fonte
Meu código de protótipo: http://ideone.com/x7hz7i
Meus pontos de design:
fonte
Existe um bom artigo em uma revista, injustamente, em alemão: http://www.dotnetpro.de/articles/onlinearticle1398.aspx
A ideia base é ter uma classe Unit como Length com uma BaseMeasurement. A classe contém o fator de conversão, sobrecargas do operador, sobrecargas ToString, analisador de strings e uma implementação como indexador. Até implementamos a visualização Architectual, mas ela não é lançada como uma biblioteca.
Então você vê o uso com o operador Pressão ou apenas:
Mas como você disse, também não encontrei o unicórnio :)
fonte
Essa é a razão de ser do
units
comando Unix , que faz tudo usando uma abordagem orientada a arquivos de dados para especificar os relacionamentos.fonte
units
. O principal motivo pelo qual as unidades não funcionam para minha solução mais ampla são as seqüências de forma livre. Concedido, ele fornece mensagens de erro em troca, mas essa abordagem está direcionada aos desenvolvedores que integrarão esse código ao nosso aplicativo. Seqüências de caracteres livres apresentam muitas oportunidades de erro.units
arquivo de dados. A maneira como define as relações entre quantidades é muito clara e pode ser útil para o seu problema.