C#:将基类转换为子类
我有一个类,NetworkClient作为基类:C#:将基类转换为子类,c#,oop,class,C#,Oop,Class,我有一个类,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 Netw
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强制转换为SkyfilterClient时。引发异常,无法将“Network.NetworkClient”类型的对象强制转换为“Network.SkyfilterClient”类型
我的代码怎么了?我看到这个流可以转换成NetworkStream,MemoryStream。为什么NetworkClient不能转换为Skyfilter客户端?在OOP中,不能将父类的实例强制转换为子类。只能将子实例强制转换为其继承自的父实例。请使用强制转换运算符,如下所示:
var skyfilterClient = (SkyfilterClient)networkClient;
您不能
downcast
。如果创建了父对象,则无法将其强制转换为子对象
一个建议的解决方法是创建父级实现的接口。如果需要,使用子覆盖功能,或者只公开父功能。将强制转换更改为接口并执行操作
编辑:也可以使用is
关键字检查对象是否为SkyfilterClient
if(networkClient is SkyfilterClient)
{
}
只要对象实际上是一个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
可以将父类的值复制到子类。例如,如果是这种情况,您可以使用反射。您可以使用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
我很惊讶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<NetworkClient, SkyfilterClient>();
var skyfilterObject = AutoMapper.Mapper.Map<SkyfilterClient>(existingNetworkClient);
使用AutoMapper;
...
//在某个地方,您的网络客户端被声明
var existingNetworkClient=new NetworkClient();
...
//现在我们想要输入cast,但是我们不能,所以我们使用AutoMapper实例化
AutoMapper.Mapper.CreateMap();
var skyfilterObject=AutoMapper.Mapper.Map(现有NetworkClient);
下面是一个全面的例子:
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;
}
}
公共级车辆
{
public int NumWheels{get;set;}
公共布尔值{get;set;}
}
公营车辆
{
公共字符串颜色{get;set;}
公共字符串SteeringColumnStyle{get;set;}
}
公营汽车制造商
{
//我得到了想要变成汽车的车辆。。。
公共列表转换(列表车辆)
{
var cars=新列表();
AutoMapper.Mapper.CreateMap();//声明我们希望发生一些automagic
foreach(车辆中的var车辆)
{
var car=自动映射器映射器映射(车辆);
//此时,特定于车辆的属性(颜色和SteeringColumnStyle)为空,因为车辆对象中没有要从中映射的属性。
//然而,由于继承而存在的汽车NumWheels和HasMotor属性由AutoMapper填充。
cars.Add(car);
}
返回车辆;
}
}
我建议您从任何子类中识别所需的功能,并创建一个通用方法来转换到正确的子类中
我也有同样的问题,但我真的不想创建一些映射类或导入库
假设您需要“Authenticate”方法从正确的子类获取行为。在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);
}
受保护的bool身份验证(字符串用户名、字符串密码){
//...
}
受保护的bool DoAuthenticate(NetworkClient nc、字符串用户名、字符串密码),其中T:NetworkClient{
//在子类中进行转换。
T subInst=(T)nc;
返回nc.Authenticate(用户名、密码);
}
如果必须,而且您不介意黑客攻击,您可以让序列化为您完成这项工作
鉴于这些类别:
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<ChildObj>(serialized);
var parent=newparentobj(){Name=“something”};
var serialized=JsonConvert.serialized对象(父对象);
var child=JsonConvert.DeserializeObject(序列化);
这假设您的对象可以很好地进行序列化,obv
请注意,这可能会比显式转换器慢。有几种方法可以做到这一点。然而,这里有一个最简单的方法可以做到这一点,而且它是可重用的
发生的事情是,我们正在获取父类的所有属性,并更新子类上的相同属性。其中baseObj是父对象,T是子类
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;
}
publicstatict ConvertToDerived(objectbaseobj),其中T:new()
{
var derivedObj=新的T();
var members=baseObj.GetType().GetMembers();
foreach(成员中的var成员)
{
对象val=null;
if(member.MemberType==MemberTypes.Field)
{
val=((FieldInfo)成员).GetValue(baseObj);
((FieldInfo)成员).SetValue(derivedObj,val);
}
else if(member.MemberType==MemberTypes.Property)
{
val=((PropertyInfo)成员).GetValue(baseObj);
if(val为IList&&val.GetType().IsGenericType)
{
var listType=val.GetType().GetGenericArguments().Single();
变量列表=(IList)Act
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;
}
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);
}
var sfcl = networkClient.ConvertToJson().ConvertToObject<SkyfilterClient>();