Registering KnownType types at runtime using IsAssignableFrom()
While doing some reflection, I hit this method IsAssignableFrom(). MSDN says:
“Determines whether an instance of the current Type can be assigned from an instance of the specified Type.”
With a boolean return type, it returns:
“true if c and the current Type represent the same type, or if the current Type is in the inheritance hierarchy of c, or if the current Type is an interface that c implements, or if c is a generic type parameter and the current Type represents one of the constraints of c. false if none of these conditions are true, or if c is null.”
If I have classes like:
1: public class Base {}
2: public class Derived1 : Base {}
3: public class SomeOther{}
4:
5: // in the calling code, I can say:
6: Derived1 derived1 = new Derived1();
7: // output: True
8: Console.WriteLine(typeof(Base).IsAssignableFrom(derived1.GetType()));
9:
10: SomeOther someOther = new SomeOther();
11: // output: False
12: Console.WriteLine(typeof(Base).IsAssignableFrom(someOther.GetType()));
Now let’s see another place where this can be used.
Say I have the below data contracts.
1: [DataContract]
2: public class BaseType
3: {
4: [DataMember]
5: public string Name { get; set; }
6:
7: public override string ToString()
8: {
9: return "BaseType: Name";
10: }
11: }
12:
13: [DataContract]
14: public class Child1 : BaseType
15: {
16: [DataMember]
17: public string Child1Property { get; set; }
18:
19: public override string ToString()
20: {
21: return "Child1: Name, Child1Property";
22: }
23: }
24:
25: [DataContract]
26: public class Child2 : BaseType
27: {
28: [DataMember]
29: public string Child2Property { get; set; }
30:
31: public override string ToString()
32: {
33: return "Child2: Name, Child2Property";
34: }
35: }
If I pass either Child1 or Child2 to my operation contract that is declared below, I get a runtime serialization error.
1: [OperationContract]
2: string GetDetails(BaseType baseType);
To avoid this, we know that we have to decorate the BaseType class with [KnownType] attribute mentioning the type of each of the derived classes.
1: [KnownType(typeof(Child1))]
2: [KnownType(typeof(Child2))]
3: [DataContract]
4: public class BaseType
5: {
6: [DataMember]
7: public string Name { get; set; }
8:
9: public override string ToString()
10: {
11: return "BaseType: Name";
12: }
13: }
This works perfectly fine, but what if there are quite a few number of such derived classes. Adding a KnownType for every such class could become cumbersome, unwieldy and unmaintainable.
You can also pass a method name that returns an IEnumerable of Types.
1: [DataContract]
2: [KnownType("GetChildTypes")]
3: public class BaseType
4: {
5: [DataMember]
6: public string Name { get; set; }
7:
8: public override string ToString()
9: {
10: return "BaseType: Name";
11: }
12:
13: public static IEnumerable<Type> GetChildTypes()
14: {
15: return from type in typeof (BaseType).Assembly.GetTypes()
16: where typeof (BaseType).IsAssignableFrom(type)
17: select type;
18: }
19: }
As you can see the GetChildTypes method scans the assembly for all the types that ‘are assignable from’ the BaseType type. By using the IsAssignableFrom() method, the code looks quite clean now.