C# : Converting Base Class to Child Class

C#OopClass

C# Problem Overview


I have a class, NetworkClient as a base class :

using System.IO;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace Network
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class NetworkClient
{
    public NetworkClient()
    {
        tcpClient = new TcpClient();
    }
    public NetworkClient(TcpClient client)
    {
        tcpClient = client;
    }

	public virtual bool IsConnected
	{
		get;
		private set;
	}
    private StreamWriter writer { get; set; }
    private StreamReader reader { get; set; }

	private TcpClient tcpClient
	{
		get;
		set;
	}

	public virtual NetworkServerInfo NetworkServerInfo
	{
		get;
		set;
	}

	public async virtual void Connect(NetworkServerInfo info)
	{
        if (tcpClient == null)
        {
            tcpClient=new TcpClient();
        }
		await tcpClient.ConnectAsync(info.Address,info.Port);
	    reader = new StreamReader(tcpClient.GetStream());
        writer = new StreamWriter(tcpClient.GetStream());
	}

	public virtual void Disconnect()
	{
		tcpClient.Close();            
        reader.Dispose();

        writer.Dispose();
	}

	public async virtual void Send(string data)
	{
		await writer.WriteLineAsync(data);
	}

	public async virtual Task<string> Receive()
	{
	    return await reader.ReadLineAsync();
	}

}
}

And also have a child class derived from NetworkClient :

using System.Net;

namespace Network
{
using Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public class SkyfilterClient : NetworkClient
{
	public virtual IPAddress Address
	{
		get;
		set;
	}

	public virtual int Port
	{
		get;
		set;
	}

	public virtual string SessionID
	{
		get;
		set;
	}

	public virtual User UserData
	{
		get;
		set;
	}

	protected virtual bool Authenticate(string username, string password)
	{
		throw new System.NotImplementedException();
	}

}
}

The problem is, that when im trying to cast NetworkClient into SkyfilterClient. An exception is thrown, Unable to cast object of type 'Network.NetworkClient' to type 'Network.SkyfilterClient'.

Whats wrong with my code ? I see that Stream can be converted to NetworkStream, MemoryStream. Why NetworkClient can't be converted to Skyfilter Client?

C# Solutions


Solution 1 - C#

As long as the object is actually a SkyfilterClient, then a cast should work. Here is a contrived example to prove this:

using System;

class Program
{
    static void Main()
    {
        NetworkClient net = new SkyfilterClient();
        var sky = (SkyfilterClient)net;
    }
}

public class NetworkClient{}
public class SkyfilterClient : NetworkClient{}

However, if it is actually a NetworkClient, then you cannot magically make it become the subclass. Here is an example of that:

using System;

class Program
{
    static void Main()
    {
        NetworkClient net = new NetworkClient();
        var sky = (SkyfilterClient)net;
    }
}

public class NetworkClient{}
public class SkyfilterClient : NetworkClient{}

HOWEVER, you could create a converter class. Here is an example of that, also:

using System;

class Program
{
    static void Main()
    {
        NetworkClient net = new NetworkClient();
        var sky = SkyFilterClient.CopyToSkyfilterClient(net);
    }
}

public class NetworkClient
{  
  public int SomeVal {get;set;}
}

public class SkyfilterClient : NetworkClient
{
    public int NewSomeVal {get;set;}
    public static SkyfilterClient CopyToSkyfilterClient(NetworkClient networkClient)
    {
        return new SkyfilterClient{NewSomeVal = networkClient.SomeVal};
    }
}

But, keep in mind that there is a reason you cannot convert this way. You may be missing key information that the subclass needs.

Finally, if you just want to see if the attempted cast will work, then you can use is:

if(client is SkyfilterClient)
    cast

Solution 2 - C#

I'm surprised AutoMapper hasn't come up as an answer.

As is clear from all the previous answers, you cannot do the typecast. However, using AutoMapper, in a few lines of code you can have a new SkyfilterClient instantiated based on an existing NetworkClient.

In essence, you would put the following where you are currently doing your typecasting:

using AutoMapper;
...
// somewhere, your network client was declared
var existingNetworkClient = new NetworkClient();
...
// now we want to type-cast, but we can't, so we instantiate using AutoMapper
AutoMapper.Mapper.CreateMap<NetworkClient, SkyfilterClient>();
var skyfilterObject = AutoMapper.Mapper.Map<SkyfilterClient>(existingNetworkClient);

Here's a full-blown example:

  public class Vehicle
  {
    public int NumWheels { get; set; }
    public bool HasMotor { get; set; }
  }

  public class Car: Vehicle
  {
    public string Color { get; set; }
    public string SteeringColumnStyle { get; set; }
  }

  public class CarMaker
  {
    // I am given vehicles that I want to turn into cars...
    public List<Car> Convert(List<Vehicle> vehicles)
    {
      var cars = new List<Car>();
      AutoMapper.Mapper.CreateMap<Vehicle, Car>(); // Declare that we want some automagic to happen
      foreach (var vehicle in vehicles)
      {
        var car = AutoMapper.Mapper.Map<Car>(vehicle);
        // At this point, the car-specific properties (Color and SteeringColumnStyle) are null, because there are no properties in the Vehicle object to map from.
        // However, car's NumWheels and HasMotor properties which exist due to inheritance, are populated by AutoMapper.
        cars.Add(car);
      }
      return cars;
    }
  }

Solution 3 - C#

If you HAVE to, and you don't mind a hack, you could let serialization do the work for you.

Given these classes:

public class ParentObj
{
    public string Name { get; set; }
}

public class ChildObj : ParentObj
{
    public string Value { get; set; }
}

You can create a child instance from a parent instance like so:

var parent = new ParentObj() { Name = "something" };
var serialized = JsonConvert.SerializeObject(parent);
var child = JsonConvert.DeserializeObject<ChildObj>(serialized);

This assumes your objects play nice with serialization, obv.

Be aware that this is probably going to be slower than an explicit converter.

Solution 4 - C#

In OOP, you can't cast an instance of a parent class into a child class. You can only cast a child instance into a parent that it inherits from.

Solution 5 - C#

There's a few ways of doing this. However, here is one of the easiest ways to do this and it's reusable.

What is happening is that we're getting all the properties of the parent class and updating those same properties on the child class. Where baseObj would be the parent object and T would be the child class.

public static T ConvertToDerived<T>(object baseObj) where T : new()
	{
		var derivedObj = new T();
		var members = baseObj.GetType().GetMembers();
		foreach (var member in members)
		{
			object val = null;
			if (member.MemberType == MemberTypes.Field)
			{
				val = ((FieldInfo)member).GetValue(baseObj);
				((FieldInfo)member).SetValue(derivedObj, val);
			}
			else if (member.MemberType == MemberTypes.Property)
			{
				val = ((PropertyInfo)member).GetValue(baseObj);
				if (val is IList && val.GetType().IsGenericType)
				{
					var listType = val.GetType().GetGenericArguments().Single();
					var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(listType));
					foreach (var item in (IList)val)
					{
						list.Add(item);
					}
					((PropertyInfo)member).SetValue(baseObj, list, null);
				}
				if (((PropertyInfo)member).CanWrite)
					((PropertyInfo)member).SetValue(derivedObj, val);
			}
		}
		return derivedObj;
	}

Solution 6 - C#

I don't think you can downcast an object, however there is a simple way to "downcast" the object outside the box. It isn't type safe, but it works. First serialize the object into json, then deserialize it into the child class object. It works the same as if you were passing the object between apis. So, while there are some people who may say "this doesn't work or isn't good", I would argue that it is exactly the way our internet currently works, so... why not use that method? No mapping required as long as parameter names are the same, and they will be if it is a child class. Note: This will likely not copy any private fields; if you have a constructor with parameters, this probably needs to be tested as well to ensure there aren't side effects.

Here's my toolbox:

public static string ConvertToJson<T>(this T obj)
{
    return JsonConvert.SerializeObject(obj);
}
public static T ConvertToObject<T>(this string json)
{
    if (string.IsNullOrEmpty(json))
    {
        return Activator.CreateInstance<T>();
    }
    return JsonConvert.DeserializeObject<T>(json);
}

Here's how to use it:

var sfcl = networkClient.ConvertToJson().ConvertToObject<SkyfilterClient>();

Solution 7 - C#

You can't downcast. If the parent object is created, it cannot be cast to the child.

One suggested workaround would be to Create an interface which the parent implements. Have the child override functionality if needed or just expose the parents functionality. Change the cast to be an interface and do the operations.

Edit: May be could also check if the object is a SkyfilterClient using is keyword

   if(networkClient is SkyfilterClient)
   {
            
   }

Solution 8 - C#

You can copy value of Parent Class to a Child class. For instance, you could use reflection if that is the case.

Solution 9 - C#

You can use the as operator to perform certain types of conversions between compatible reference types or nullable types.

SkyfilterClient c = client as SkyfilterClient;
if (c != null)
{
    //do something with it
}

 

NetworkClient c = new SkyfilterClient() as NetworkClient; // c is not null
SkyfilterClient c2 = new NetworkClient() as SkyfilterClient; // c2 is null

Solution 10 - C#

I would recommend identifying the functionality you need from any subclasses, and make a generic method to cast into the right subclass.

I had this same problem, but really didn't feel like creating some mapping class or importing a library.

Let's say you need the 'Authenticate' method to take behavior from the right subclass. In your NetworkClient:

protected bool Authenticate(string username, string password) {
  //...
}
protected bool DoAuthenticate<T>(NetworkClient nc, string username, string password) where T : NetworkClient {
//Do a cast into the sub class.
  T subInst = (T) nc;
  return nc.Authenticate(username, password);
}

Solution 11 - C#

Use the cast operator, as such:

var skyfilterClient = (SkyfilterClient)networkClient;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAditya HeartlyView Question on Stackoverflow
Solution 1 - C#Justin PihonyView Answer on Stackoverflow
Solution 2 - C#HeyZikoView Answer on Stackoverflow
Solution 3 - C#ChaosView Answer on Stackoverflow
Solution 4 - C#TombalaView Answer on Stackoverflow
Solution 5 - C#VashView Answer on Stackoverflow
Solution 6 - C#Patrick KnottView Answer on Stackoverflow
Solution 7 - C#jacob aloysiousView Answer on Stackoverflow
Solution 8 - C#KrishnaDhunganaView Answer on Stackoverflow
Solution 9 - C#DamithView Answer on Stackoverflow
Solution 10 - C#balthatrixView Answer on Stackoverflow
Solution 11 - C#Stephen ClearyView Answer on Stackoverflow