既知のデータ コントラクト型

KnownTypeAttribute クラスを使用すると、逆シリアル化において考慮する必要のある型を事前に指定できます。 実施例については、「 Known Types 」の例を参照してください。

通常は、クライアントとサービス間でパラメーターを渡したり、値を返したりするときに、転送するデータのデータ コントラクトのすべてが両方のエンドポイントで共有されます。 ただし、次の場合はこれが該当しません。

  • 送信データ コントラクトが、予想データ コントラクトから派生する場合。 詳細については、「データ コントラクトの等価性」の継承に関するセクションを参照してください。 この場合、送信されるデータには、受信エンドポイントで予想しているデータ コントラクトが含まれません。

  • クラス、構造体、または列挙とは対照的に、送信される情報の宣言型がインターフェイスである場合。 したがって、インターフェイスを実装するどの型が実際に送信されるかを事前に知ることができないため、受信エンドポイントでは送信されるデータのデータ コントラクトを事前に確認することができません。

  • 送信される情報の宣言型が Objectである場合。 すべての型が Objectから継承され、どの型が実際に送信されるかを事前に知ることができないため、受信エンドポイントでは送信されるデータのデータ コントラクトを事前に確認することができません。 これは最初の項目についての特殊なケースです。すべてのデータ コントラクトは既定で、 Objectに対して生成された空のデータ コントラクトから派生します。

  • .NET Framework 型を含む一部の型に、前述した 3 つのカテゴリの 1 つに分類されるメンバーが含まれる場合。 たとえば、 HashtableObject を使用して、ハッシュ テーブルに実際のオブジェクトを保存します。 これらの型をシリアル化する場合、受信側ではこれらのメンバーのデータ コントラクトを事前に確認することができません。

KnownTypeAttribute クラス

データが受信エンドポイントに到着すると、WCF ランタイムにより、共通言語ランタイム (CLR: Common Language Runtime) 型のインスタンスへのデータの逆シリアル化が試行されます。 逆シリアル化するためにインスタンス化される型は、まず受信メッセージを調べてメッセージの内容が従うデータ コントラクトを特定することで選択されます。 次に逆シリアル化エンジンは、メッセージの内容と互換性のあるデータ コントラクトを実装する CLR 型を探します。 逆シリアル化エンジンによってこの処理中に逆シリアル化の候補の型として許可される一連の型は、逆シリアル化の "既知の型" のセットと呼ばれます。

逆シリアル化エンジンに型の情報を知らせる方法として、 KnownTypeAttributeを使用する方法があります。 この属性は、データ コントラクト型全体に適用できるだけで、個々のデータ メンバーには適用できません。 この属性は、クラスまたは構造体にすることが可能な 外部型 に適用されます。 最も基本的な使用方法として、この属性を適用すると、型が "既知の型" として指定されます。これにより、外部型のオブジェクトまたはメンバーを介して参照されるオブジェクトが逆シリアル化されるときに、常にその既知の型が既知の型のセットに含まれるようになります。 複数の KnownTypeAttribute 属性を同じ型に適用することができます。

既知の型とプリミティブ

プリミティブとして扱われる特定の型 ( DateTimeXmlElementなど) と同様に、プリミティブ型は、常に "既知" であり、このメカニズムを通して追加する必要がありません。 ただし、プリミティブ型の配列は明示的に追加する必要があります。 ほとんどのコレクションは、配列と同等に扱われます。 (非ジェネリック コレクションは、 Objectの配列と同等に扱われます)。 プリミティブ、プリミティブ配列、およびプリミティブ コレクションの使用例については、例 4 を参照してください。

Note

他のプリミティブ型とは異なり、 DateTimeOffset 構造は、既定では既知の型ではないため、既知の型のリストに手動で追加する必要があります。

KnownTypeAttribute クラスの使用例を次に示します。

例 1

継承関係にある 3 つのクラスがあります。

[DataContract]
public class Shape { }

[DataContract(Name = "Circle")]
public class CircleType : Shape { }

[DataContract(Name = "Triangle")]
public class TriangleType : Shape { }
<DataContract()> _
Public Class Shape
End Class

<DataContract(Name:="Circle")> _
Public Class CircleType
    Inherits Shape
End Class
<DataContract(Name:="Triangle")> _
Public Class TriangleType
    Inherits Shape
End Class

次の CompanyLogo クラスはシリアル化できますが、 ShapeOfLogo メンバーが CircleType オブジェクトと TriangleType オブジェクトのどちらかに設定されている場合は、逆シリアル化エンジンが "Circle" や "Triangle" という名前のデータ コントラクト型を認識しないため、逆シリアル化することはできません。

[DataContract]
public class CompanyLogo
{
    [DataMember]
    private Shape ShapeOfLogo;
    [DataMember]
    private int ColorOfLogo;
}
<DataContract()> _
Public Class CompanyLogo
    <DataMember()> _
    Private ShapeOfLogo As Shape
    <DataMember()> _
    Private ColorOfLogo As Integer
End Class

CompanyLogo 型の正しいコードの記述方法は次のとおりです。

[DataContract]
[KnownType(typeof(CircleType))]
[KnownType(typeof(TriangleType))]
public class CompanyLogo2
{
    [DataMember]
    private Shape ShapeOfLogo;
    [DataMember]
    private int ColorOfLogo;
}
<DataContract(), KnownType(GetType(CircleType)), KnownType(GetType(TriangleType))> _
Public Class CompanyLogo2
    <DataMember()> _
    Private ShapeOfLogo As Shape
    <DataMember()> _
    Private ColorOfLogo As Integer
End Class

外部型の CompanyLogo2 が逆シリアル化されるときは、必ず、逆シリアル化エンジンが CircleTypeTriangleType を認識するため、"Circle" データ コントラクトおよび "Triangle" データ コントラクトと一致する型を検出できます。

例 2

次の例では、 CustomerTypeACustomerTypeB の両方が Customer データ コントラクトを持っている場合でも、逆シリアル化エンジンは CustomerTypeB だけを認識するため、 PurchaseOrder が逆シリアル化されるときは必ず CustomerTypeB のインスタンスが作成されます。

public interface ICustomerInfo
{
    string ReturnCustomerName();
}

[DataContract(Name = "Customer")]
public class CustomerTypeA : ICustomerInfo
{
    public string ReturnCustomerName()
    {
        return "no name";
    }
}

[DataContract(Name = "Customer")]
public class CustomerTypeB : ICustomerInfo
{
    public string ReturnCustomerName()
    {
        return "no name";
    }
}

[DataContract]
[KnownType(typeof(CustomerTypeB))]
public class PurchaseOrder
{
    [DataMember]
    ICustomerInfo buyer;

    [DataMember]
    int amount;
}
Public Interface ICustomerInfo
    Function ReturnCustomerName() As String
End Interface

<DataContract(Name:="Customer")> _
Public Class CustomerTypeA
    Implements ICustomerInfo
    Public Function ReturnCustomerName() _
    As String Implements ICustomerInfo.ReturnCustomerName
        Return "no name"
    End Function
End Class

<DataContract(Name:="Customer")> _
Public Class CustomerTypeB
    Implements ICustomerInfo
    Public Function ReturnCustomerName() _
    As String Implements ICustomerInfo.ReturnCustomerName
        Return "no name"
    End Function
End Class

<DataContract(), KnownType(GetType(CustomerTypeB))> _
Public Class PurchaseOrder
    <DataMember()> _
    Private buyer As ICustomerInfo

    <DataMember()> _
    Private amount As Integer
End Class

例 3

次の例では、 HashtableObjectとして内部的にその内容を保存します。 ハッシュ テーブルを正常に逆シリアル化するには、逆シリアル化エンジンがそのテーブルに現れる可能性のある型のセットを認識している必要があります。 この場合は、 Book オブジェクトと Magazine オブジェクトだけが Catalogに保存されているため、この 2 つが KnownTypeAttribute 属性を使用して追加されることが事前にわかっています。

[DataContract]
public class Book { }

[DataContract]
public class Magazine { }

[DataContract]
[KnownType(typeof(Book))]
[KnownType(typeof(Magazine))]
public class LibraryCatalog
{
    [DataMember]
    System.Collections.Hashtable theCatalog;
}
<DataContract()> _
Public Class Book
End Class

<DataContract()> _
Public Class Magazine
End Class

<DataContract(), KnownType(GetType(Book)), KnownType(GetType(Magazine))> _
Public Class LibraryCatalog
    <DataMember()> _
    Private theCatalog As System.Collections.Hashtable
End Class

例 4

次の例では、データ コントラクトには数値と、その数値に基づいて実行する演算が含まれています。 Numbers データ メンバーは、整数、整数の配列、または整数を含む List<T> にすることができます。

注意事項

この例は、WCF プロキシを生成するために SVCUTIL.EXE が使用されている場合にのみ、クライアント側で機能します。 SVCUTIL.EXE は、サービスからメタデータ (任意の既知の型を含む) を取得します。 この情報がない場合、クライアントは型を逆シリアル化できません。

[DataContract]
[KnownType(typeof(int[]))]
public class MathOperationData
{
    private object numberValue;
    [DataMember]
    public object Numbers
    {
        get { return numberValue; }
        set { numberValue = value; }
    }
    //[DataMember]
    //public Operation Operation;
}
<DataContract(), KnownType(GetType(Integer()))> _
Public Class MathOperationData
    Private numberValue As Object

    <DataMember()> _
    Public Property Numbers() As Object
        Get
            Return numberValue
        End Get
        Set(ByVal value As Object)
            numberValue = value
        End Set
    End Property
End Class

アプリケーション コードは次のとおりです。

// This is in the service application code:
static void Run()
{

    MathOperationData md = new MathOperationData();

    // This will serialize and deserialize successfully because primitive
    // types like int are always known.
    int a = 100;
    md.Numbers = a;

    // This will serialize and deserialize successfully because the array of
    // integers was added to known types.
    int[] b = new int[100];
    md.Numbers = b;

    // This will serialize and deserialize successfully because the generic
    // List<int> is equivalent to int[], which was added to known types.
    List<int> c = new List<int>();
    md.Numbers = c;
    // This will serialize but will not deserialize successfully because
    // ArrayList is a non-generic collection, which is equivalent to
    // an array of type object. To make it succeed, object[]
    // must be added to the known types.
    ArrayList d = new ArrayList();
    md.Numbers = d;
}
' This is in the service application code:
Shared Sub Run()
    Dim md As New MathOperationData()
    ' This will serialize and deserialize successfully because primitive 
    ' types like int are always known.
    Dim a As Integer = 100
    md.Numbers = a

    ' This will serialize and deserialize successfully because the array of 
    ' integers was added to known types.
    Dim b(99) As Integer
    md.Numbers = b

    ' This will serialize and deserialize successfully because the generic 
    ' List(Of Integer) is equivalent to Integer(), which was added to known types.
    Dim c As List(Of Integer) = New List(Of Integer)()
    md.Numbers = c
    ' This will serialize but will not deserialize successfully because 
    ' ArrayList is a non-generic collection, which is equivalent to 
    ' an array of type object. To make it succeed, object[]
    ' must be added to the known types.
    Dim d As New ArrayList()
    md.Numbers = d

End Sub

既知の型、継承、およびインターフェイス

KnownTypeAttribute 属性を使用して既知の型を特定の型に関連付けると、その既知の型がその特定の型から派生したすべての型に関連付けられます。 たとえば、次のコードを参照してください。

[DataContract]
[KnownType(typeof(Square))]
[KnownType(typeof(Circle))]
public class MyDrawing
{
    [DataMember]
    private object Shape;
    [DataMember]
    private int Color;
}

[DataContract]
public class DoubleDrawing : MyDrawing
{
    [DataMember]
    private object additionalShape;
}
<DataContract(), KnownType(GetType(Square)), KnownType(GetType(Circle))> _
Public Class MyDrawing
    <DataMember()> _
    Private Shape As Object
    <DataMember()> _
    Private Color As Integer
End Class

<DataContract()> _
Public Class DoubleDrawing
    Inherits MyDrawing
    <DataMember()> _
    Private additionalShape As Object
End Class

DoubleDrawing クラスは、 KnownTypeAttribute フィールドで SquareCircle を使用するために AdditionalShape 属性を必要としません。これは、既に基本クラス (Drawing) にこれらの属性が適用されているためです。

既知の型は、クラスと構造体にのみ関連付けることができます。インターフェイスに関連付けることはできません。

オープン ジェネリック メソッドを使用する既知の型

既知の型としてジェネリック型の追加が必要な場合があります。 ただし、オープン ジェネリック型をパラメーターとして KnownTypeAttribute 属性に渡すことはできません。

この問題は、型の一覧を返すメソッドを作成して既知の型のコレクションに追加するという代替機構を使用することで解決できます。 次に、いくつかの制限事項があるため、このメソッドの名前を KnownTypeAttribute 属性への文字列引数として指定します。

このメソッドは、 KnownTypeAttribute 属性を適用する型上に存在し、静的で、パラメーターを必要とせず、 IEnumerableTypeに代入可能なオブジェクトを返す必要があります。

同じ型上で、メソッド名を持つ KnownTypeAttribute 属性と実際の型を持つ KnownTypeAttribute 属性を組み合わせることはできません。 また、メソッド名が同じ複数の KnownTypeAttribute を同じ型に適用することはできません。

次のクラスを参照してください。

[DataContract]
public class DrawingRecord<T>
{
    [DataMember]
    private T theData;
    [DataMember]
    private GenericDrawing<T> theDrawing;
}
<DataContract()> _
Public Class DrawingRecord(Of T)
    <DataMember()> _
    Private theData As T
    <DataMember()> _
    Private theDrawing As GenericDrawing(Of T)
End Class

theDrawing フィールドには、 ColorDrawing ジェネリック クラスおよび BlackAndWhiteDrawingジェネリック クラスのインスタンスを格納でき、これらのクラスは両方とも Drawingジェネリック クラスから継承されています。 通常、両クラスとも既知の型に追加する必要がありますが、次の属性の構文は無効です。

// Invalid syntax for attributes:  
// [KnownType(typeof(ColorDrawing<T>))]  
// [KnownType(typeof(BlackAndWhiteDrawing<T>))]  
' Invalid syntax for attributes:  
' <KnownType(GetType(ColorDrawing(Of T))), _  
' KnownType(GetType(BlackAndWhiteDrawing(Of T)))>  

したがって、これらの型を返すメソッドを作成する必要があります。 この型の正しいコードの記述方法は次のとおりです。

[DataContract]
[KnownType("GetKnownType")]
public class DrawingRecord2<T>
{
    [DataMember]
    private T TheData;
    [DataMember]
    private GenericDrawing<T> TheDrawing;

    private static Type[] GetKnownType()
    {
        Type[] t = new Type[2];
        t[0] = typeof(ColorDrawing<T>);
        t[1] = typeof(BlackAndWhiteDrawing<T>);
        return t;
    }
}
<DataContract(), KnownType("GetKnownType")> _
Public Class DrawingRecord2(Of T)
    Private TheData As T
    Private TheDrawing As GenericDrawing(Of T)

    Private Shared Function GetKnownType() As Type()
        Dim t(1) As Type
        t(0) = GetType(ColorDrawing(Of T))
        t(1) = GetType(BlackAndWhiteDrawing(Of T))
        Return t
    End Function
End Class

既知の型を追加するその他の方法

また、既知の型は、構成ファイルを使用して追加することもできます。 この方法は、Windows Communication Foundation (WCF) でサードパーティ製の型ライブラリを使用する場合のように、適切な逆シリアル化のために既知の型を必要とする型を制御できない場合に便利です。

構成ファイルで既知の型を指定する方法を次に示します。

<configuration>

<system.runtime.serialization>

<dataContractSerializer>

<declaredTypes>

<add type="MyCompany.Library.Shape,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL">

<knownType type="MyCompany.Library.Circle,

MyAssembly, Version=2.0.0.0, Culture=neutral,

PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>

</add>

</declaredTypes>

</dataContractSerializer>

</system.runtime.serialization>

</configuration>

前の構成ファイルでは、 MyCompany.Library.Shape というコントラクト型が MyCompany.Library.Circle を既知の型として持つと宣言されています。

関連項目