You cannot define WCF contracts that rely on generic type parameters. Generics are specific to .NET, and using them would violate the service-oriented nature of WCF. However, you can use bounded generic types in your data contracts, as long as you specify the type parameters in the service contract and as long as the specified type parameters have valid data contracts, as shown in Example 3-15.
Example 3-15. Using bounded generic types
[DataContract]
class MyClass<T>
{
[DataMember]
public T MyMember;
}
[ServiceContract]
interface IMyContract
{
[OperationContract]
void MyMethod(MyClass<int>
obj);
}
When you import the metadata of a data contract such as the one in Example 3-15, the imported types have all type parameters replaced with specific types, and the data contract itself is renamed to this format:
<Original name>Of<Type parameter names><hash>
Using the same definitions as in Example 3-15, the imported data contract and service contract will look like this:
[DataContract] class MyClassOfint
{int
MyMemberField; [DataMember] publicint
MyMember { get { return MyMemberField; } set { MyMemberField = value; } } } [ServiceContract] interface IMyContract { [OperationContract] void MyMethod(MyClassOfint
obj); }
If, however, the service contract were to use a custom type such as SomeClass
instead of int
:
[DataContract]
class SomeClass
{...}
[DataContract]
class MyClass<T>
{...}
[OperationContract]
void MyMethod(MyClass<SomeClass>
obj);
the exported data contract might look like this:
[DataContract] class SomeClass {...} [DataContract] class MyClassOfSomeClassMTRdqN6P
{...} [OperationContract(...)] void MyMethod(MyClassOfSomeClassMTRdqN6P
obj);
where MTRdqN6P
is some quasi-unique hash of the
generic type parameter and the containing namespace. Different data contracts and namespaces
will generate different hashes. The hash is in place to reduce the overall potential for a
conflict with another data contract that might use another type parameter with the same
name. No hash is created for the implicit data contracts of the primitives when they are
used as generic type parameters, since the type int
is a
reserved word and the definition of MyClassOfint
is
unique.
In most cases, the hash is a cumbersome over-precaution. You can specify a different
name for the exported data contract by simply assigning it to the data contract's Name
property. For example, given this service-side data
contract:
[DataContract] class SomeClass {...} [DataContract(Name = "MyClass"
)] class MyClass<T> {...} [OperationContract] void MyMethod(MyClass<SomeClass>
obj);
the exported data contract will be:
[DataContract]
class SomeClass
{...}
[DataContract]
class MyClass
{...}
[OperationContract]
void MyMethod(MyClass
obj);
However, by doing this you run the risk of some ambiguity, since two different custom generic types will result in the same type name.
If you would still like to combine the name of the generic type parameter with that of
the data contract, use the {<number>}
directive,
where the number is the ordinal number of the type parameter. For example, given this
service-side definition:
[DataContract]
class SomeClass
{...}
[DataContract(Name = "MyClassOf{0}{1}
")]
class MyClass<T,U>
{...}
[OperationContract]
void MyMethod(MyClass<SomeClass,int> obj);
the exported definition will be:
[DataContract] class SomeClass {...} [DataContract] class MyClassOfSomeClassint
{...} [OperationContract(...)] void MyMethod(MyClassOfSomeClassint
obj);
The number of type parameters specified is not verified at compile time. Any mismatch will yield a runtime exception.
Finally, you can append #
after the number to
generate the unique hash. For example, given this data contract definition:
[DataContract]
class SomeClass
{...}
[DataContract(Name = "MyClassOf{0}{#}
")]
class MyClass<T>
{...}
[OperationContract]
void MyMethod(MyClass<SomeClass> obj);
the exported definition will be:
[DataContract] class SomeClass {...} [DataContract] class MyClassOfSomeClassMTRdqN6P
{...} [OperationContract] void MyMethod(MyClassOfSomeClassMTRdqN6P
obj);
13.58.116.51