5

I have 2 types: BaseQuestion and Question. Question inherits some properties from BaseQuestion. Now I have created a Web API to make a BaseQuestion available. The Question datatype has additional properties that I do not want to make available. I have a method that retrieves a Question and my initial plan was to just implicitly upcast it to BaseQuestion. I thought it would lose all extra properties that I do not want to make accessible and I could return it. Well, it doesn't. This is what I do:

Question q = allQuestions[0];
BaseQuestion bq = q;
string type = bq.GetType().ToString();

The type of bq is still "Question". I cannot access the BaseQuestion properties, but I can still see them in the debugger and they are in the JSON output that I send to the client.

Any ideas on how I can "force" bq to be of type BaseQuestion and not to have any properties that are defined in the subclass?

aravind
  • 535
  • 4
  • 21
Jan
  • 2,168
  • 2
  • 19
  • 28
  • The type of the variable bq is BaseQuestion. The runtime type of the object to which bq refers is Question. Not sure what you mean by "force bq to be of type BaseQuestion". You will only be able to access BaseQuestion members via the variable bq. – lesscode Aug 24 '12 at 02:48
  • 1
    I think your confusion is not understanding that what you've given are **references** to objects in memory. – Simon Whitehead Aug 24 '12 at 03:00
  • Simon, good point. Then let me rephrase: How can I create a new object of type Question and automatically copy all property values? By automatically I mean that I do **not** want to implement it. IMHO this scenario is not at all unique to my situation and should be covered by .NET. – Jan Aug 24 '12 at 03:42

2 Answers2

11

Typecasting doesn't change the nature of the object, it only changes your view of the object. When you typecast to the base type, you're looking at the object through a filter that can only see the members defined on the base type, regardless of whatever else is defined in the actual object.

When you return an object from a web service call, it is the actual object that will be serialized and sent back across the wire - all of its serializable members.

One technique you could use to prevent members of the derived Question class from being returned to the API caller is to suppress serialization of the members declared in the Question class. There are several serialization subsystems in .NET, but if you're using XmlSerialization you would decorate the members declared in the Question class with [XmlIgnore] attributes to prevent them from being serialized by XmlSerialization. If you're using a different serialization subsystem you will need to figure out the analog of this in that system.

Another possibility is to define interface contracts for your web API service(s). Your BaseQuestion and Question classes would implement one or more of those contract interfaces. I believe this will limit the serialization to only the properties defined in the contract interface, regardless of what the actual object is.

If all else fails, the brute force solution is to construct an instance of your BaseQuestion in a temp var, copy the relevant properties from the actual Question object to the temp object, and return the temp object. This is rude and crude and there's got to be a better way, but it will work.

dthorpe
  • 35,318
  • 5
  • 75
  • 119
  • I cannot change the Question/BaseQuestion types as the Web API is not their only user. I totally agree on the "there's got to be a better way" part, and I have been looking all over the place but came up empty. I think my frustration is big enough to create an explicit converter. But thanks for the help! – Jan Aug 24 '12 at 03:34
  • Most of this answer is correct, except for the part about limiting serialization based on interface inheritance. That part doesn't work because it doesn't change the original object. I wish it worked like that though :( – Travis J Jun 05 '15 at 00:04
  • Rather than creating a "contract interface", create a "contract model" of whatever you want to serialize and deserialize. Write conversion functions or use a library like [AutoMapper](https://github.com/AutoMapper/AutoMapper) to convert your "full" model to the slimmed-down version that only contains the properties you want to serialize. – MarioDS Oct 26 '17 at 09:45
1

Typecasting only changes the type of the reference to the object, not the object itself.

Calling .GetType() on an instance of Question will always return the Question type regardless of the variable type that references it.

If you want the base type then you need to call the .BaseType property on the type.

So, something like this:

string type = q.GetType().BaseType.ToString();
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • That was only to show what I mean. I need to have an object that _only_ contains the properties of the base class. – Jan Aug 24 '12 at 03:26
  • Isn't that what type casting to `BaseQuestion` will give you? – Enigmativity Aug 24 '12 at 03:49
  • That's what I thought, but apparently it does not. I do not change the object itself, I only change the container I store the reference in. The properties of the subclass are still there and will get serialized. – Jan Aug 24 '12 at 03:53
  • Oh, I see. You need to implement an external serializer factory for each of your types. But, of course, if you serialize a `Question` as a `BaseQuestion`, for example, you most likely couldn't deserialize it back to `Question`. – Enigmativity Aug 24 '12 at 04:31