Unable to cast Base class (data contract) to derived class

Nirman picture Nirman · May 6, 2013 · Viewed 12.9k times · Source
[DataContract]
public class SearchCriteria
{
    [DataMember]
    public string CountryID { get; set; }    
}

[DataContract]
public class CitySearchCriteria: SearchCriteria
{
    [DataMember]
    public string CityID { get; set; }
}

I am creating an instance of SearchCriteria in my MVC controller action, and trying to convert it into CitySearchCriteria.

SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.CountryID = "1";
CitySearchCriteria citySearchCriteria = searchCriteria as CitySearchCriteria;

The "citySearchCriteria" object after the above statement is showing NULL value. I was expecting it to show both properties, CountryID and CityID with CountryID populated, and CityID blank... but it is setting the object to NULL.

What could be the solution here? Has DataContract to do anything with this?

The comments are suggesting, you cannot convert a base to derive: but actually, I have done this successfully in my view, its just not working in controller action:

CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;

This is converting successfully, so why not the similar thing working in controller action?

Answer

Martin Mulder picture Martin Mulder · May 6, 2013

You cannot cast this way!

If you do new you create a new memory object of a certain size. In your case new SearchCriteria() creates a new memory object with enough size to hold one string, nothing more, nothing less.

In your last line you do searchCriteria as CitySearchCriteria trying to cast the object in searchCriteria to a larger type CitySearchCriteria. But it cannot be done. You are trying to 'convert' a memory object which holds 1 string into a memory object that can hold 2 strings. But casting does not convert an new memory object. What would be the value of the new string? It simply looks under water to check if your reference searchCriteria already contains an object of type CitySearchCriteria. In your case: it does not (the object is of type SearchCriteria) and returns null.

So... the next example DOES work (because CitySearchCriteria has already been created). This is also your solution:

SearchCriteria searchCriteria = new CitySearchCriteria(); 
CitySearchCriteria citySearchCriteria = searchCriteria as CitySearchCriteria;

And this does not work (because CitySearchCriteria has NOT already been created). This is your situation:

SearchCriteria searchCriteria = new SearchCriteria();
CitySearchCriteria citySearchCriteria = searchCriteria as CitySearchCriteria;

It is the same thing as the next example.
This does work (because SearchCriteria has already been created):

object o = new SearchCriteria();
SearchCriteria searchCriteria = o as SearchCriteria;

And this does not (because SearchCriteria has NOT already been created)::

object o = new object();
SearchCriteria searchCriteria = o as SearchCriteria;

For the record: I would always use a direct cast, not a cast using as, unless you want to explicitly test if an object is of that type.