Unable to cast Base class (data contract) to derived class
Asked Answered
F

3

11
[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?

Fluted answered 6/5, 2013 at 5:56 Comment(3)
You cannot cast from Base to Derived. The inverse is possibleMilker
please see last two paragraph in the questionFluted
Check again Martin's answer (1st example).Milker
V
11

Everybody has already (and correctly) told you that you simply can't cast from Base to Derived, but it seems to me that you still don't get the reason why this line works in another chunk of your code:

CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;

I think that you are a little bit confused about what the "Type" of an instance is. You didn't post the definition of Model, but I think that you have something like this:

public SearchCriteria SearchCriteria;

This doesn't mean that SearchCriteria always contains instances of SearchCriteria, but only that it contains instances of types that can be cast to SearchCriteria. In your case it can contain instances of SearchCriteria or of CitySearchCriteria. I suppose that somewhere in your code you will find something like:

Model.SearchCriteria = new CitySearchCriteria();

and this is what allows yor cast to be executed correctly. You can see that the instance is indeed a CitySearchCriteria (and not simply an instance of SearchCriteria) executing this code just before the cast:

MessageBox.Show(Model.SearchCriteria.GetType().FullName);

To understand better you could try to modify the value in SearchCriteria just before your working cast as shown below only to find out that the cast won't work anymore:

Model.SearchCriteria = new SearchCriteria();
MessageBox.Show(Model.SearchCriteria.GetType().FullName);
CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;
Volsci answered 6/5, 2013 at 10:9 Comment(1)
thank you, yes, you are right, I had a lot of confusion over this. these details have clarified the same, and I could see where I am wrong in coding this. ThanksFluted
B
13

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.

Britneybritni answered 6/5, 2013 at 6:3 Comment(7)
It just cannot be converted because, based on his implementation, the cast won't work from the base class to the derived one. The inverse is possible.Milker
I think that is just what I said... isn't it? Or am I missing what you mean? What example in my text is not correct?Britneybritni
Yes, the third will work. Object can hold ANY type. And you can cast it to any type as long as it holds that type. Another example would be this: object o = "Hello"; string s = o as string; Why do you think it will not work?Britneybritni
That is not what I meant. Where is the solution to his question?Milker
The solution is example #1.Britneybritni
Then I give you back the +1 :)Milker
Thanks for keeping me sharp and awake! :)Britneybritni
V
11

Everybody has already (and correctly) told you that you simply can't cast from Base to Derived, but it seems to me that you still don't get the reason why this line works in another chunk of your code:

CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;

I think that you are a little bit confused about what the "Type" of an instance is. You didn't post the definition of Model, but I think that you have something like this:

public SearchCriteria SearchCriteria;

This doesn't mean that SearchCriteria always contains instances of SearchCriteria, but only that it contains instances of types that can be cast to SearchCriteria. In your case it can contain instances of SearchCriteria or of CitySearchCriteria. I suppose that somewhere in your code you will find something like:

Model.SearchCriteria = new CitySearchCriteria();

and this is what allows yor cast to be executed correctly. You can see that the instance is indeed a CitySearchCriteria (and not simply an instance of SearchCriteria) executing this code just before the cast:

MessageBox.Show(Model.SearchCriteria.GetType().FullName);

To understand better you could try to modify the value in SearchCriteria just before your working cast as shown below only to find out that the cast won't work anymore:

Model.SearchCriteria = new SearchCriteria();
MessageBox.Show(Model.SearchCriteria.GetType().FullName);
CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria;
Volsci answered 6/5, 2013 at 10:9 Comment(1)
thank you, yes, you are right, I had a lot of confusion over this. these details have clarified the same, and I could see where I am wrong in coding this. ThanksFluted
B
2

You could create CitySearchCriteria, and cast it to SearchCriteria. This way you can only see CountryId. Later, you can cast it back to CitySearchCriteria, and see CountryId and CityId.

This has nothing to do with DataContract. Solution in your case would be to create CitySearchCriteria and cast it to SearchCriteria (if you need it).

Benefactress answered 6/5, 2013 at 6:4 Comment(2)
I found two similar questions: #989158 and #9886144Benefactress
The thing is that, my view is expecting a "SearchCriteria" object because it been called from multiple places, which can have "CitySearchCriteria", "StateSearchCriteria", etc depending upon the calling controller and action. Also, I have successfully done this inside my views, like, CitySearchCriteria citySearchCriteria = (CitySearchCriteria)Model.SearchCriteria; and that is working fine, so why not in controller action?Fluted

© 2022 - 2024 — McMap. All rights reserved.