C#:将基类转换为子类

我有一个类,NetworkClient作为基类:

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 Receive() { return await reader.ReadLineAsync(); } } } 

并且还有一个从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(); } } } 

问题是,当我试图将NetworkClient转换为SkyfilterClient时。 抛出exception,无法将类型为“Network.NetworkClient”的对象强制转换为“Network.SkyfilterClient”。

我的代码有什么问题? 我看到Stream可以转换为NetworkStream,MemoryStream。 为什么无法将NetworkClient转换为Skyfilter客户端?

只要该对象实际上是一个SkyfilterClient ,那么演员应该可以工作。 这是一个人为的例子来certificate这一点:

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

但是,如果它实际上是一个NetworkClient ,那么你就不能神奇地使它成为子类。 这是一个例子:

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

但是,您可以创建转换器类。 这是一个例子,也是:

 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}; } } 

但是,请记住,有一个原因你无法通过这种方式进行转换。 您可能缺少子类所需的关键信息。

最后,如果您只想查看尝试的强制转换是否有效,那么您可以使用is

 if(client is SkyfilterClient) cast 

我很惊讶AutoMapper还没有作为答案。

从以前的所有答案可以清楚地看出,你无法进行类型转换。 但是,使用AutoMapper ,在几行代码中,您可以根据现有的NetworkClient实例化一个新的SkyfilterClient

从本质上讲,您可以将以下内容放在当前进行类型转换的位置:

 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(); var skyfilterObject = AutoMapper.Mapper.Map(existingNetworkClient); 

这是一个完整的例子:

  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 Convert(List vehicles) { var cars = new List(); AutoMapper.Mapper.CreateMap(); // Declare that we want some automagic to happen foreach (var vehicle in vehicles) { var car = AutoMapper.Mapper.Map(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; } } 

在OOP中,您不能将父类的实例强制转换为子类。 您只能将子实例强制转换为它inheritance的父实例。

你不能downcast 。 如果创建了父对象,则无法将其强制转换为子对象。

一个建议的解决方法是创建父实现的interface 。 如果需要,让子代覆盖function,或者只显示父代function。 将转换更改为接口并执行操作。

编辑:也可以使用is关键字检查对象是否是SkyfilterClient

  if(networkClient is SkyfilterClient) { } 

您可以将父类的值复制到Child类。 例如,如果是这种情况,您可以使用reflection。

如果你必须,并且你不介意黑客,你可以让序列化为你做的工作。

鉴于这些类:

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

您可以从父实例创建子实例,如下所示:

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

假设您的对象在序列化方面表现良好,obv。

请注意,这可能比显式转换器慢。 仅此示例需要约300毫秒。

您可以使用as运算符在兼容的引用类型或可空类型之间执行某些类型的转换。

 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 

使用强制转换操作符,如下:

 var skyfilterClient = (SkyfilterClient)networkClient; 

我建议从任何子类中识别您需要的function,并制作一个通用方法来强制转换为正确的子类。

我有同样的问题,但真的不想创建一些映射类或导入库。

假设你需要’Authenticate’方法来从正确的子类中获取行为。 在您的NetworkClient中:

 protected bool Authenticate(string username, string password) { //... } protected bool DoAuthenticate(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); }