8

特定の時間(値と時間のタプル)で値を公開するための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);
        }        
    }    
}
4

5 に答える 5

1

あなたの提案したアプローチは私には合理的であるように思われます、そして私は私のプロジェクトで同様のものを使用します。ただし、オブジェクトの実際の値の部分を保持し、自然に値型であるため、structの代わりに使用します。classここでは継承は必要ありません(とにかく構造体では不可能です)ので、インターフェイスを使用してコントラクトを作成し、必要に応じて制約として機能します(私はそれを呼び出しましたIUnitOfMeasure)。

さまざまなタイプの測定のすべての単位を組み合わせて1つの列挙型を作成することはお勧めしません。長さを操作するときに誰かが質量単位を参照していないことを確認するために、単位を検証するのは地獄です。

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;

于 2012-05-28T22:56:26.830 に答える
1

これは不可能です。列挙型はプリミティブ型であり、継承はオブジェクトのプロパティであるため、他の列挙型から継承することはできません。

于 2012-05-28T22:59:04.537 に答える
0

列挙型で継承を導入することはできません。列挙型は、コード内で意味のあるテキスト識別子を使用できるようにするための便利なメカニズムです。あなたが持っているコードから、次のような列挙型を使用することをお勧めします。

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

または、適切な場所に分割して、たとえば、質量と長さが別々に定義されるようにします。

「継承」は、この問題についてのあなたの考えに導入されたものに過ぎませんが、あなたの解決には必要ありません。Mass を処理する場合は、Mass に適したフラグ/列挙型のみを調べます。

于 2012-05-28T23:18:10.417 に答える