C#:将基类转换为子类
Posted
技术标签:
【中文标题】C#:将基类转换为子类【英文标题】:C# : Converting Base Class to Child Class 【发布时间】:2013-05-08 04:58:18 【问题描述】:我有一个类,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”类型。
我的代码有什么问题?我看到Stream可以转换为NetworkStream,MemoryStream。为什么 NetworkClient 不能转为 Skyfilter Client?
【问题讨论】:
【参考方案1】:如果您必须这样做,并且您不介意 hack,您可以让序列化为您完成这项工作。
鉴于这些类:
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);
这假设您的对象可以很好地进行序列化,obv。
请注意,这可能会比显式转换器慢。
【讨论】:
这很好用,太吓人了。我用它做一些调试,非常有帮助。 为了避免第三方库,json(反)序列化在 System.Web.Script.Serialization 中,请参阅 javascriptSerializer 我从不相信黑客 这是一个 hack,但它不是一个 hack……除了它是一个 hack,但它不是真正的 hack。说实话,有时 JSON 序列化/反序列化在一些关键的边缘情况下确实有帮助,在这些情况下,用 C# 做事情的“自然方式”要复杂得多。对于那些边缘情况(包括这个),没关系 - 只要代码实际上需要开始执行类似的操作。【参考方案2】:有几种方法可以做到这一点。但是,这是执行此操作的最简单方法之一,并且可以重复使用。
我们正在获取父类的所有属性并更新子类的相同属性。其中 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;
【讨论】:
它在某种程度上有效,但它不适用于 IList 字段,因为它们都以 0 计数进行转换。 @AmirHajiha 我已经修改了代码以帮助处理 IList 字段。【参考方案3】:我不认为您可以向下转换对象,但是有一种简单的方法可以在框外“向下转换”对象。它不是类型安全的,但它可以工作。先将对象序列化成json,再反序列化成子类对象。它的工作原理与您在 api 之间传递对象一样。所以,虽然有些人可能会说“这不起作用或不好”,但我认为这正是我们的互联网目前的工作方式,所以......为什么不使用这种方法呢?只要参数名称相同,就不需要映射,如果它是子类,它们将是相同的。注意:这可能不会复制任何私有字段;如果您有带参数的构造函数,则可能还需要对其进行测试以确保没有副作用。
这是我的工具箱:
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>();
【讨论】:
【参考方案4】:我很惊讶 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<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;
【讨论】:
除了良好的健康多态性理论之外,很高兴看到有人承认在实践中有时必须编写这样的代码。拥有一个出色的工具不仅可以节省工作量,还可以防止错误! Automapper 是个好工具,谢谢提醒! CreateMap 已被弃用。有关 AutoMapper 的新用法,请参阅本文。 ***.com/questions/38194012/…【参考方案5】:我建议从任何子类中确定您需要的功能,并创建一个通用方法来转换为正确的子类。
我遇到了同样的问题,但真的不想创建一些映射类或导入库。
假设您需要 '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);
【讨论】:
【参考方案6】:只要对象实际上是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 SkyfilterClientNewSomeVal = networkClient.SomeVal;
但是,请记住,您不能以这种方式转换是有原因的。您可能缺少子类所需的关键信息。
最后,如果你只是想看看尝试的演员阵容是否有效,那么你可以使用is
:
if(client is SkyfilterClient)
cast
【讨论】:
您好,我尝试了您的示例并进行了一些更改。它确实将父对象转换为子对象 - 但它没有将任何父属性迁移到子对象。对于我的代码,我期待一种将父对象转换为子对象并迁移父属性(子属性将为空)的方法。有什么想法吗? @HemantTank 你最好的办法是诚实地提出一个单独的问题。我一直没有维护这些旧问题中的一些。 谢谢。现在我正在使用基于 JSON 的装箱/拆箱技术。不完美但适合我的目的 - ***.com/questions/8329470/… 不要忘记,从 C# 7.0 开始,您可以这样做if(client is SkyfilterClient filterClient) // use filterClient
【参考方案7】:
您可以使用 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
【讨论】:
【参考方案8】:您可以将父类的值复制到子类。例如,如果是这种情况,您可以使用反射。
【讨论】:
【参考方案9】:你不能downcast
。如果创建了父对象,则不能将其强制转换为子对象。
一个建议的解决方法是创建一个父级实现的interface
。如果需要,让孩子覆盖功能,或者只公开父母的功能。将强制转换为接口并执行操作。
编辑:也可以使用is
关键字检查对象是否为SkyfilterClient
if(networkClient is SkyfilterClient)
【讨论】:
【参考方案10】:使用强制转换运算符,例如:
var skyfilterClient = (SkyfilterClient)networkClient;
【讨论】:
我已经这样做了,当我做 Authenticate((SkyfilterClient)client) 时出现错误 我已经这样做了,当我做 Authenticate((SkyfilterClient)client) 时出现错误 在这种情况下,您的client
实际上可能不是 SkyfilterClient
。【参考方案11】:
在 OOP 中,您不能将父类的实例强制转换为子类。您只能将子实例转换为它继承自的父实例。
【讨论】:
以上是关于C#:将基类转换为子类的主要内容,如果未能解决你的问题,请参考以下文章