Какой шаблон я должен использовать для выражения Иерархического Перечисления?

Я экспериментирую с API для публикации значений в определенный момент времени (кортежи значений и времени). Эти образцы будут использоваться средством просмотра данных (например, графиком).

Я хочу связать значение с количеством и единицей, например, длиной в метрах. Таким образом, мой «зритель» может масштабировать его соответствующим образом.

Я ищу своего рода иерархическое перечисление, например:

enum Quantity
{
   Mass.Kg,
   Mass.g,
   Length.m,
   Length.mm
}

Но этого не существует в C #.

Я не уверен, что лучший способ выразить это, и я придумал следующее. Есть признанный или лучший способ сделать это?

using System;
using Moq;

namespace ConsoleApplication26
{
    class Program
    {
        static void Main(string[] args)
        {
            //use a Mock to play with the API
            Mock<ITelemetryPublisherFactory> mockTelemetryPublisherFactory = new Mock<ITelemetryPublisherFactory>();
            var telemetryPublisherFactory = mockTelemetryPublisherFactory.Object;

            //example usages
            var massTelemetryPublisher = telemetryPublisherFactory.GetChannelSamplePublisher<Double>("My Mass", Mass.Kg);
            massTelemetryPublisher.PublishChannelSampleAtTimeNow(83.4);                        

            var lengthTelemetryPublisher = telemetryPublisherFactory.GetChannelSamplePublisher<Int32>("My Height", Length.μm);
            lengthTelemetryPublisher.PublishChannelSampleAtTimeNow(1800000);      

            //10 years time..
            lengthTelemetryPublisher.PublishChannelSampleAtTimeNow(1800000);
            massTelemetryPublisher.PublishChannelSampleAtTimeNow(120.1);                        
        }
    }

    public interface ITelemetryPublisherFactory
    {
        ITelemetryPublisher<T> GetChannelSamplePublisher<T>(String channelName, Quantity quantity);
    }

    public interface ITelemetryPublisher<T>
    {
        void PublishChannelSampleAtTimeNow(T sampleValue);
    }

    public abstract class Quantity {}

    public class Mass : Quantity
    {
        private enum Unit
        {
            g,
            Kg
        }

        private readonly Unit _unit;

        private Mass(Unit unit)
        {
            _unit = unit;
        }

        public static Quantity Kg {get { return new Mass(Unit.Kg); }}
        public static Quantity g { get { return new Mass(Unit.g); } }

        public override string ToString()
        {
            return String.Format("Mass.{0}", _unit);
        }
    }

    public class Length : Quantity
    {
        private enum Unit
        {
            m,
            mm,
            μm,
            beardSecond
        }

        private readonly Unit _unit;

        private Length(Unit unit)
        {
            _unit = unit;
        }

        public static Quantity m { get { return new Length(Unit.m); } }
        public static Quantity mm { get { return new Length(Unit.mm); } }
        public static Quantity μm { get { return new Length(Unit.μm); } }
        public static Quantity beardSecond { get { return new Length(Unit.beardSecond); } }

        public override string ToString()
        {
            return String.Format("Length.{0}", _unit);
        }        
    }    
}
7 голосов | спросил Daniel James Bryars 29 Mayam12 2012, 02:24:33

5 ответов


0

Я думаю, что лучше создать класс Unit для единицы измерения и Quantity класс, который связывает единицу измерения с суммой. Посмотрите на структуру количества этой идеи. Поскольку вы также хотите записать «тип» единицы измерения, вы можете создать класс UnitType, который записывает эту информацию:

public sealed partial class UnitType {
  public string Name { get; private set; }
  public UnitType(string name) {
    Name = name;
  }
}

public sealed partial class Unit {
  public string Name { get; private set; }
  public UnitType Type { get; private set; }
  public Unit(string name, UnitType type) {
    Name = name;
    Type = type;
  }
}

(Вы должны сделать их правильными типами значений, переопределив Equals и GetHashCode)

Класс Unit может быть расширен, например, для обеспечения преобразования, составные единицы, форматирование и анализ.

Затем вы можете определить общие случаи внутри классов:

public partial class UnitType {
  public static readonly UnitType Mass = new UnitType("Mass");
  public static readonly UnitType Length = new UnitType("Length");
}

public partial class Unit {
  public static readonly Unit Grams = new Unit("g", UnitType.Mass);
  public static readonly Unit Kilos = new Unit("kg", UnitType.Mass);
  // ...
}

Или определите ваши "иерархии" со статическими классами:

public static class Mass {
  public static readonly UnitType Type = new UnitType("Mass");

  public static readonly Unit Grams = new Unit("g", Type);
  public static readonly Unit Kilos = new Unit("kg", Type);
  ...
}

public static class Length ...

Класс Quantity также будет иметь тип неизменяемого значения (просто показывает его использование):

var eniacWeight = new Quantity(27, Mass.Tons);

Или вы можете использовать методы расширения для создания Quantity s:

var eniacWeight = 27.Tons();

(из ENIAC )

ответил Jordão 29 Mayam12 2012, 03:23:36
0

Это невозможно. Перечисления являются примитивными типами и не могут наследоваться от других перечислений, поскольку наследование является свойством объектов.

ответил Porco 29 Mayam12 2012, 02:59:04
0

Иерархическое перечисление невозможно, как отмечено выше. Если вы используете исключительно метрику, вы можете использовать стандартные префиксы, если это поможет.

enum MeasurementUnits
{
    Gram,
    Metre,
    Litre,
    Hectare
    // etc
}

enum MeasurementPrefix
{
    Milli,
    Natural,
    Kilo,
    Mega
    // etc
}

Это может быть не совсем то, что вам нужно, но это обеспечит тип «группировки», который вы можете искать (например, групповые измерения, касающиеся длины, веса и т. д., путем проверки значения «единиц»). р>

ответил Kirk Broadhurst 29 Mayam12 2012, 03:24:08
0

Ваш предложенный подход кажется мне разумным, и я использую нечто подобное в моем проекте. Однако я сохраняю фактическую часть значения объекта и использую struct вместо class, поскольку они являются типами значений. Наследование здесь не обязательно (и в любом случае невозможно со структурами), поэтому я использую интерфейс для создания договора и при необходимости выступаю в качестве ограничения (я назвал его IUnitOfMeasure).

Я не рекомендую создавать одно перечисление со всеми единицами измерения различных типов; это адски проверяет единицу, чтобы убедиться, что кто-то не ссылается на единицу массы при работе с Length.

public interface IUnitOfMeasure<TThis>
    where TThis : IUnitOfMeasure<TThis>
{
    TThis ConvertTo(TThis value);
}

public struct Mass : IUnitOfMeasure<Mass>
{
    public enum Units
    {
        Gram,
        Kilogram
    }

    private double _value;
    private Mass.Units _unit;

    public double Value { get { return _value; } }

    public Mass.Units Unit { get { return _unit; } }

    public Mass(double value, Mass.Units unit)
    {
        _value = value;
        _unit = unit;
    }

    public Mass ConvertTo(Mass value)
    {
        switch(value.Unit)
        {
            case Units.Gram:
                return new Mass(Unit == Units.Gram ? Value : Value/1000, Units.Gram);
            case Units.Kilogram:
                return new Mass(Unit == Units.Gram ? Value*1000 : Value, Units.Kilogram);
            default:
                throw new NotImplementedException();
        }
    }

    public override string ToString()
    {
        return string.Format("{0} {1}", Value, Unit);
    }

    public static readonly Mass G = new Mass(0, Units.Gram);
    public static readonly Mass Kg = new Mass(0, Units.Kilogram);
}

Использование:

var kg = new Mass(5.0, Mass.Units.Kilogram);

Console.WriteLine(kg); // writes "5 Kilogram"

var g = kg.ConvertTo(Mass.G);

Console.WriteLine(g); // writes ".005 Gram"

Если вы не заботитесь о сохранении значения и просто хотите хранить перечислимые /статические значения в центральном месте:

public static class UnitOfMeasure
{
    public enum Mass
    {
      Gram,
      Kilogram
    }

    public enum Length
    {
       Meter,
       Kilometer
    }
    // etc.
}

Использование: var unit = UnitOfMeasure.Mass.Kilogram;

ответил HackedByChinese 29 Mayam12 2012, 02:56:26
0

Нельзя вводить наследование с помощью перечислений. Перечисления - это просто удобный механизм, позволяющий вам использовать значимые текстовые идентификаторы в вашем коде. Из кода, который вы имеете, я предлагаю вам использовать перечисление как;

public enum UnitOfMeasure
{
    MassGrams,
    MassKg,
    LengthMM,
    LengthCM,
    . . .
}

Или разделите его там, где это уместно, чтобы масса и длина определялись, например, отдельно.

«Наследование» - это то, что вы ввели в свои мысли об этой проблеме, но это не обязательно для вашего решения. Когда вы хотите иметь дело с Масса, вы смотрите только на флаги /перечисления, соответствующие массе.

ответил RJ Lohan 29 Mayam12 2012, 03:18:10

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132