2

アプリケーションに次のクラス構造があります。

[ProtoContract]
public abstract class WebSyncedObject
{
    [ProtoMember(1)]
    public DateTime SystemTime { get; set; }

    [ProtoMember(2)]
    public bool TimeSynchronized { get; set; }

    [ProtoMember(3)]
    public ulong RelativeTime { get; set; }

    [ProtoMember(4)]
    public Guid BootID { get; set; }

    protected WebSyncedObject()
    {
        BootID = BootID.GetBootID();
        if (BootID == Guid.Empty) return;

        TimeSynchronized = Time.TimeSynchronized;
        RelativeTime = Time.RelativeTime;
        SystemTime = DateTime.Now;
    }
}

[ProtoContract]
public class GPSReading : WebSyncedObject
{
    [ProtoMember(1)]
    public DateTime SatelliteTime { get; set; }

    [ProtoMember(2)]
    public decimal Latitude { get; set; }

    [ProtoMember(3)]
    public decimal Longitude { get; set; }

    [ProtoMember(4)]
    public int NumSatellites { get; set; }

    [ProtoMember(5)]
    public decimal SpeedKM { get; set; }
}

[ProtoContract]
public class TemperatureReading : WebSyncedObject
{
    [ProtoMember(1)]
    public decimal Temperature { get; set; }

    [ProtoMember(2)]
    public int NodeID { get; set; }

    [ProtoMember(3)]
    public string ProbeIdentifier { get; set; }
}

次に、両方のタイプのデータを使用して List<WebSyncedObject> を作成し、次の例外が発生したときに Protobuf-net でシリアル化を試みます。

InvalidOperationException 予期しないサブタイプ: Logger.TemperatureReading

ProtoInclude 属性について読んだことがありますが、コードを簡単に拡張できるようにする必要があるため、それを使用したくありません。それを自動的に生成することに関する警告も見られました。

拡張可能にしながらこれを達成する方法はありますか?

4

1 に答える 1

1

最終的には、一意の識別子 (フィールド番号) を使用して特定のサブタイプ (GPSReadingなど) をライブラリが識別する、堅牢で信頼性が高く、繰り返し可能な方法が必要です。多くの場合、これを行う最も便利な方法は属性を使用することです。ただし、これがオプションでない場合は、実行時にこれを行うこともできます。おそらく、構成ファイルの識別子を読み取ります。(実行時に)「利用可能なすべてのサブタイプを見つけ、アルファベット順に並べ替え、(たとえば) 10 からインクリメントする」とだけ言うのは良い考えではありませんAltitudeReadingすべての数を変更し、既存のデータを破壊します。ただし、これらを繰り返し可能な方法で定義できる限り、それならすべて良いです。たとえば、属性で...

[ProtoInclude(10, typeof(GPSReading))]
[ProtoInclude(11, typeof(TemperatureReading))]
[ProtoInclude(12, typeof(AltitudeReading))]

ただし、テキスト ファイルまたは xml 構成ファイルで何かを行うこともできます...多分:

<add key="10" type="Some.Namespace.GPSReading"/>
<add key="11" type="Some.Namespace.TemperatureReading"/>
<add key="12" type="Some.Namespace.AltitudeReading"/>

構成ファイルを読み取り、次を呼び出す独自のコードを追加します。

int key = int.Parse(element.GetAttributeValue("key"));
Type type = someAssembly.GetType(element.GetAttributeValue("type"));
RuntimeTypeModel.Default[typeof(WebSyncedObject)].AddSubType(key, type);

繰り返しますが、重要なことは、各サブタイプに関連付けられた数値が将来的に確実に再現可能でなければならないということです。保証できる限り、属性を使用する必要はありません。ただし、モデルは識別子を知る必要があります。

于 2013-09-09T11:48:29.820 に答える