KnownTypeAttribute クラスを使用すると、逆シリアル化時に考慮する必要がある型を事前に指定できます。 作業例については、「 既知の型 」の例を参照してください。
通常、クライアントとサービスの間でパラメーターと戻り値を渡すとき、両方のエンドポイントは、送信されるデータのすべてのデータ コントラクトを共有します。 ただし、次の状況では、これは当たりません。
送信されたデータ コントラクトは、想定されるデータ コントラクトから派生します。 詳細については、「 データ コントラクトの等価性」の継承に関するセクションを参照してください。 その場合、送信されるデータは、受信エンドポイントで想定されるデータ コントラクトと同じではありません。
送信される情報の宣言された型は、クラス、構造体、または列挙型ではなく、インターフェイスです。 そのため、インターフェイスを実装する型が実際に送信されるのを事前に把握できないため、受信エンドポイントは、送信されたデータのデータ コントラクトを事前に判断できません。
送信する情報の宣言された型が Object。 すべての型は Objectから継承され、実際に送信される型を事前に把握できないため、受信エンドポイントは送信されたデータのデータ コントラクトを事前に判断できません。 これは、最初の項目の特殊なケースです。すべてのデータ コントラクトは、既定の空のデータ コントラクトから派生し、 Object用に生成されます。
.NET Framework 型を含む一部の型には、上記の 3 つのカテゴリのいずれかに属するメンバーがあります。 たとえば、 Hashtable では、 Object を使用して、実際のオブジェクトをハッシュ テーブルに格納します。 これらの型をシリアル化する場合、受信側は、これらのメンバーのデータ コントラクトを事前に決定できません。
KnownTypeAttribute クラス
受信エンドポイントにデータが到着すると、WCF ランタイムは共通言語ランタイム (CLR) 型のインスタンスにデータを逆シリアル化しようとします。 逆シリアル化のためにインスタンス化される型は、最初に受信メッセージを調べて、メッセージの内容が準拠するデータ コントラクトを決定することによって選択されます。 逆シリアル化エンジンは、メッセージの内容と互換性のあるデータ コントラクトを実装する CLR 型の検索を試みます。 このプロセス中に逆シリアル化エンジンで許可される候補型のセットは、デシリアライザーの "既知の型" のセットと呼ばれます。
逆シリアル化エンジンに型について知らせる方法の 1 つは、 KnownTypeAttributeを使用することです。 この属性は、個々のデータ メンバーには適用できず、データ コントラクト型全体にのみ適用できます。 属性は、クラスまたは構造体にすることができる 外部型 に適用されます。 最も基本的な使用法では、属性を適用すると、型が "既知の型" として指定されます。これにより、外部型のオブジェクトまたはそのメンバーを介して参照されるオブジェクトが逆シリアル化されるたびに、既知の型が既知の型のセットの一部になります。 同じ型に複数の KnownTypeAttribute 属性を適用できます。
既知の型とプリミティブ
プリミティブ型だけでなく、プリミティブとして扱われる特定の型 ( DateTime や XmlElementなど) は常に "既知" であり、このメカニズムを介して追加する必要はありません。 ただし、プリミティブ型の配列を明示的に追加する必要があります。 ほとんどのコレクションは配列と同等と見なされます。 (非ジェネリック コレクションは、 Objectの配列と同等と見なされます)。 プリミティブ、プリミティブ配列、およびプリミティブコレクションの利用例については、例 4 を参照してください。
注
他のプリミティブ型とは異なり、 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
クラスはシリアル化できますが、逆シリアル化エンジンがデータ コントラクト名 "Circle" または "Triangle" を持つ型を認識しないため、 ShapeOfLogo
メンバーが CircleType
または TriangleType
オブジェクトに設定されている場合は逆シリアル化できません。
[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
が逆シリアル化されるたびに、逆シリアル化エンジンは CircleType
と TriangleType
について認識するため、"Circle" データ コントラクトと "Triangle" データ コントラクトの一致する型を見つけることができます。
例 2
次の例では、CustomerTypeA
とCustomerTypeB
の両方に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
次の例では、 Hashtable はコンテンツを内部的に Objectとして格納します。 ハッシュ テーブルを正常に逆シリアル化するには、逆シリアル化エンジンが、そこで発生する可能性のある型のセットを認識している必要があります。 この場合、 Book
オブジェクトと Magazine
オブジェクトのみが Catalog
に格納されるため、 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>を指定できます。
注意事項
これは、SVCUTIL.EXEを使用して WCF プロキシを生成する場合にのみ、クライアント側で機能します。 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
クラスでは、Square
フィールドでCircle
とAdditionalShape
を使用するDrawing
属性は必要ありません。
既知の型は、インターフェイスではなく、クラスと構造体にのみ関連付けることができます。
Open Generic メソッドを使用した既知の型
ジェネリック型を既知の型として追加することが必要な場合があります。 ただし、開いているジェネリック型をパラメーターとして KnownTypeAttribute
属性に渡すことはできません。
この問題は、別のメカニズムを使用して解決できます。既知の型コレクションに追加する型の一覧を返すメソッドを記述します。 メソッドの名前は、いくつかの制限があるため、 KnownTypeAttribute
属性の文字列引数として指定されます。
メソッドは、KnownTypeAttribute
属性が適用される型に存在する必要があり、静的である必要があり、パラメーターを受け入れず、IEnumerableのTypeに割り当てることができるオブジェクトを返す必要があります。
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
宣言されています。