我有一个类,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客户端?
答案 0 :(得分:41)
只要对象实际上是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
答案 1 :(得分:35)
我很惊讶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;
}
}
答案 2 :(得分:6)
在OOP中,您无法将父类的实例强制转换为子类。您只能将子实例转换为它继承的父实例。
答案 3 :(得分:4)
你不能downcast
。如果创建了父对象,则无法将其强制转换为子对象。
一个建议的解决方法是创建父实现的interface
。如果需要,让子代覆盖功能,或者只显示父代功能。将演员表更改为界面并执行操作。
修改:也可以使用SkyfilterClient
关键字检查对象是否为is
if(networkClient is SkyfilterClient)
{
}
答案 4 :(得分:4)
有几种方法可以做到这一点。但是,这是执行此操作的最简单方法之一,并且可重用。
正在发生的事情是,我们正在获取父类的所有属性,并在子类上更新这些相同的属性。其中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;
}
答案 5 :(得分:3)
如果你必须这样做,而且你不介意黑客攻击,你可以让序列化为你完成工作。
鉴于这些课程:
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。
请注意,这可能比显式转换器慢。仅此示例需要约300毫秒。
答案 6 :(得分:2)
您可以将父类的值复制到Child类。例如,如果是这种情况,您可以使用反射。
答案 7 :(得分:2)
我认为您不能向下投射对象,但是有一种简单的方法可以在框外“向下投射”对象。它不是类型安全的,但是可以。首先将对象序列化为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>();
答案 8 :(得分:1)
您可以使用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
答案 9 :(得分:0)
使用强制转换操作符,如下:
var skyfilterClient = (SkyfilterClient)networkClient;
答案 10 :(得分:0)
我建议从任何子类中识别您需要的功能,并制作一个通用方法来强制转换为正确的子类。
我遇到了同样的问题,但实际上并不想创建一些映射类或导入库。
假设你需要'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);
}