Reputation: 5690
I'm currently receiving a serialized object through a socket connection, this works flawlessly. I'm deserializing it fine also. Now, I want the client to send more than one object type (or class) through the network.
This is how i deserialize my object onto it's Type 'MyClass':
var m = new MemoryStream(Convert.FromBase64String(dataReceived));
var b = new BinaryFormatter();
var o = (MyClass)b.Deserialize(m);
Now, if I'd like to receive more than 1 object type I could either make a method that can cast it to any type supported or send a header which will tell me the type the object is and then cast it. I prefer the FIRST way.
The thing is, I can't cast it with GetType() of course, and using the name property would result in something with a switch of strings which I dislike. So I decided I could do either:
object objectReceived = b.Deserialize(m);
if(o is MyClass){
MyClass myClass = (MyClass) objectReceived;
}
else if (o is AnotherClass){
AnotherClass myOtherClass = (AnotherClass) objectReceived;
}
(I could also do something with the as operator, but that will make me check for nulls afterwards. For example)
var MyClass = objectReceived as MyClass;
var AnotherClass = objectReceived as AnotherClass
OR I could implement an interface that every object sent through the network will implement, cast the object as that interface, which will return it's type, and then cast it accordingly!
The thing is, I already have that working code, but I can't figure out a clean way to code that interface. What should the interface's method return? A type? A string? I don't know really. This is why I tell myself: well, as I have working code, if it's good practice and easy to maintain, I won't get myself into the trouble of making an interface which I don't know how to now because of time constraints.
So that's my question: Is my approach correct/good practice? Or is the interface approach better?
Thanks a lot for your help guys!!
Upvotes: 0
Views: 236
Reputation: 43743
I think, potentially, the source of your problem is that you are trying to do too many things in one method. Either your method is doing the same thing with all the different types of objects (in which case they should all share the same interface and you should cast them all to that), or, as I suspect, you are doing different things with each of them, in which case it's not good practice to combine all that logic into one method, or even one class, for that matter.
So, if you had methods like this:
private void processObject(MyClass obj) { //... }
private void processObject(AnotherClass obj) { //... }
Then, in your method that deserializes the objects, you could simply check the class name from the message header, and then call the right method:
switch(className)
{
case ClassNameEnum.MyClass: processObject((MyClass)b.Deserialize(m)); break;
case ClassNameEnum.AnotherClass: processObject((AnotherClass)b.Deserialize(m)); break;
}
As I show in this example, using an enumeration is better than using strings to specify the type in the message header. It's smaller data, a faster comparison, and doesn't allow for misspellings. If you do want to use strings so that the messages are more readable, that's fine, but you should use constants for the strings.
Using a generic method to deserialize the objects is an excellent suggestion, in which case the switch code would look like this:
switch(className)
{
case ClassNameEnum.MyClass: processObject(Deserialize<MyClass>(m)); break;
case ClassNameEnum.AnotherClass: processObject(Deserialize<AnotherClass>(m)); break;
}
However, doing so doesn't really alleviate anything. You still have to add a separate case
statement for each type. If you want to keep strong-typing, there is no magic bullet to get rid of this extra coding. The whole point of casting is to inform the compiler which type you are using at compile time. If there was some way to cast it to a type that is determined at run-time, it would be utterly pointless. If you don't care about the advantages of strong-typing, you could make it a dynamic
variable, in which case you could use any type without casting it at all (this requires .NET 4).
Upvotes: 2
Reputation: 52788
Why not use Generics?
You can create a common deserialization method like so:
private T MyDeserialize<T>(String dataReceived)
{
var m = new MemoryStream(Convert.FromBase64String(dataReceived));
var b = new BinaryFormatter();
return (T)b.Deserialize(m);
}
Any then call it like:
MyClass myClassInstance = MyDeserialize<MyClass>(dataReceived);
Generics
With generics when you call MyDeserialize<MyClass>(dataReceived);
you make the method think that T
is MyClass
.
Imagine it doing this:
private MyClass MyDeserialize(String dataReceived)
{
var m = new MemoryStream(Convert.FromBase64String(dataReceived));
var b = new BinaryFormatter();
return (MyClass)b.Deserialize(m);
}
Obviously you can pass any Type (class if you prefer) you like in between the <>
's and it will use that type instead.
Upvotes: 6
Reputation: 8482
There is no much difference which casting you will use. The only difference between:
x = (Type) deserialized;
and
x = deserialized as Type;
is that the first one will raise an InvalidCastException if deserialized is not of Type, while the second will return null in x
.
As @DaveShaw explained - using generics is a very good approach, but it depends on your architecture if there are no better ways to do what you want.
Upvotes: 1