C#:转换基类子类(C# : Converting Base Class to Child Clas

2019-09-03 00:58发布

我有一个类,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<string> 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。 则抛出异常,无法转换类型“Network.NetworkClient”的目的为类型“Network.SkyfilterClient”。

什么错我的代码? 我看到流可以被转换成的NetworkStream,MemoryStream的。 为什么NetworkClient不能转换到Skyfilter客户端?

Answer 1:

只要对象实际上是一个SkyfilterClient ,然后浇铸应该工作。 这是一个人为的例子来证明这一点:

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


Answer 2:

我很惊讶AutoMapper还没有上来作为一个答案。

如从以前所有的答案清楚,你不能做类型转换。 然而,使用AutoMapper ,在几行代码,你可以有一个新的SkyfilterClient基于现有实例NetworkClient

从本质上讲,你会把下面,你正在做你的类型转换:

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

这里有一个全面的例子:

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


Answer 3:

在OOP中,你可以不投父类的实例到子类。 您只能投一个子实例到它从继承父。



Answer 4:

你不能downcast 。 如果创建父对象,它不能被投射到孩子。

一个建议的解决方法是创建一个interface ,其母公司实现。 如果需要,让孩子覆盖功能,或只是暴露父母的功能。 更改投是一个界面做的操作。

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

   if(networkClient is SkyfilterClient)
   {

   }


Answer 5:

如果你有,你不介意一个黑客,你可以让系列化做的工作适合你。

考虑到这些类:

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

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

您可以从像这样父实例孩子instace:

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

这是假设你的对象发挥不错系列化,OBV。

要知道,这大概会比一个明确的转换速度较慢。 这个例子本身就需要〜300毫秒。



Answer 6:

您可以父类的值复制到子类。 例如,你可以使用反射,如果是这样的话。



Answer 7:

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

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


Answer 8:

使用转换运算符,因为这样的:

var skyfilterClient = (SkyfilterClient)networkClient;


Answer 9:

我会建议你识别从任何子类所需要的功能,使一个通用的方法来丢在正确的子类。

我有同样的问题,但真的没有觉得自己创造了一些映射类或导入库。

比方说,你所需要的“身份验证”的方法把从右边的子类的行为。 在您的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);
}


文章来源: C# : Converting Base Class to Child Class
标签: c# oop class