C#:将基类转换为子类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16534253/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# : Converting Base Class to Child Class
提问by Aditya Heartly
I have a class, NetworkClient as a base 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 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();
}
}
}
And also have a child class derived from NetworkClient :
还有一个从 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();
}
}
}
The problem is, that when im trying to cast NetworkClient into SkyfilterClient. An exception is thrown, Unable to cast object of type 'Network.NetworkClient' to type 'Network.SkyfilterClient'.
问题是,当我尝试将 NetworkClient 转换为 SkyfilterClient 时。抛出异常,无法将“Network.NetworkClient”类型的对象转换为“Network.SkyfilterClient”类型。
Whats wrong with my code ? I see that Stream can be converted to NetworkStream, MemoryStream. Why NetworkClient can't be converted to Skyfilter Client?
我的代码有什么问题?我看到Stream可以转换为NetworkStream,MemoryStream。为什么 NetworkClient 无法转换为 Skyfilter Client?
采纳答案by Justin Pihony
As long as the object is actually a SkyfilterClient, then a cast should work. Here is a contrived example to prove this:
只要对象实际上是 a SkyfilterClient,那么演员表就应该起作用。这是一个人为的例子来证明这一点:
using System;
class Program
{
static void Main()
{
NetworkClient net = new SkyfilterClient();
var sky = (SkyfilterClient)net;
}
}
public class NetworkClient{}
public class SkyfilterClient : NetworkClient{}
However, if it is actually a NetworkClient, then you cannot magically make it become the subclass. Here is an example of that:
但是,如果它实际上是 a NetworkClient,那么您不能神奇地使其成为子类。这是一个例子:
using System;
class Program
{
static void Main()
{
NetworkClient net = new NetworkClient();
var sky = (SkyfilterClient)net;
}
}
public class NetworkClient{}
public class SkyfilterClient : NetworkClient{}
HOWEVER, you could create a converter class. Here is an example of that, also:
但是,您可以创建一个转换器类。这是一个例子,还有:
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};
}
}
But, keep in mind that there is a reason you cannot convert this way. You may be missing key information that the subclass needs.
但是,请记住,您无法以这种方式进行转换是有原因的。您可能缺少子类所需的关键信息。
Finally, if you just want to see if the attempted cast will work, then you can use is:
最后,如果您只想查看尝试的演员表是否有效,那么您可以使用is:
if(client is SkyfilterClient)
cast
回答by Tombala
In OOP, you can't cast an instance of a parent class into a child class. You can only cast a child instance into a parent that it inherits from.
在 OOP 中,您不能将父类的实例转换为子类。您只能将子实例转换为它继承的父实例。
回答by Stephen Cleary
Use the cast operator, as such:
使用强制转换运算符,如下所示:
var skyfilterClient = (SkyfilterClient)networkClient;
回答by jacob aloysious
You can't downcast. If the parent object is created, it cannot be cast to the child.
你不能downcast。如果创建了父对象,则无法将其强制转换为子对象。
One suggested workaround would be to Create an interfacewhich the parent implements. Have the child override functionality if needed or just expose the parents functionality. Change the cast to be an interface and do the operations.
一种建议的解决方法是创建一个interface父级实现的对象。如果需要,让孩子覆盖功能,或者只公开父母的功能。将转换更改为接口并执行操作。
Edit:May be could also check if the object is a SkyfilterClientusing iskeyword
编辑:也可以检查对象是否是SkyfilterClientusingis关键字
if(networkClient is SkyfilterClient)
{
}
回答by KrishnaDhungana
You can copy value of Parent Class to a Child class. For instance, you could use reflection if that is the case.
您可以将父类的值复制到子类。例如,如果是这种情况,您可以使用反射。
回答by Damith
You can use the as operator to perform certain types of conversions between compatible reference types or nullable types.
您可以使用 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
回答by HeyZiko
I'm surprised AutoMapper hasn't come up as an answer.
我很惊讶 AutoMapper 没有作为答案出现。
As is clear from all the previous answers, you cannot do the typecast. However, using AutoMapper, in a few lines of code you can have a new SkyfilterClientinstantiated based on an existing NetworkClient.
从之前的所有答案中可以清楚地看出,您不能进行类型转换。但是,使用AutoMapper,只需几行代码,您就可以SkyfilterClient基于现有的NetworkClient.
In essence, you would put the following where you are currently doing your typecasting:
本质上,您可以将以下内容放在您当前进行类型转换的位置:
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);
Here's a full-blown example:
这是一个完整的例子:
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;
}
}
回答by balthatrix
I would recommend identifying the functionality you need from any subclasses, and make a generic method to cast into the right subclass.
我建议从任何子类中确定您需要的功能,并创建一个通用方法来转换为正确的子类。
I had this same problem, but really didn't feel like creating some mapping class or importing a library.
我有同样的问题,但真的不想创建一些映射类或导入库。
Let's say you need the 'Authenticate' method to take behavior from the right subclass. In your NetworkClient:
假设您需要 '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);
}
回答by Chaos
If you HAVE to, and you don't mind a hack, you could let serialization do the work for you.
如果您必须这样做,并且您不介意黑客攻击,您可以让序列化为您完成这项工作。
Given these classes:
鉴于这些类:
public class ParentObj
{
public string Name { get; set; }
}
public class ChildObj : ParentObj
{
public string Value { get; set; }
}
You can create a child instace from a parent instance like so:
您可以从父实例创建子实例,如下所示:
var parent = new ParentObj() { Name = "something" };
var serialized = JsonConvert.SerializeObject(parent);
var child = JsonConvert.DeserializeObject<ChildObj>(serialized);
This assumes your objects play nice with serialization, obv.
这假设您的对象在序列化方面表现良好,obv。
Be aware that this is probably going to be slower than an explicit converter. This example alone takes ~300ms.
请注意,这可能比显式转换器慢。仅此示例就需要大约 300 毫秒。
回答by Vash
There's a few ways of doing this. However, here is one of the easiest ways to do this and it's reusable.
有几种方法可以做到这一点。但是,这是执行此操作的最简单方法之一,并且可以重复使用。
What is happening is that we're getting all the properties of the parent class and updating those same properties on the child class. Where baseObj would be the parent object and T would be the child class.
发生的事情是我们正在获取父类的所有属性并更新子类上的相同属性。其中 baseObj 将是父对象,T 将是子类。
public static T ConvertToDerived<T>(object baseObj) where T : new()
{
var derivedObj = new T();
var props = baseObj.GetType().GetProperties();
foreach (var prop in props)
{
if (prop.CanWrite)
{
var val = prop.GetValue(baseObj);
if (val != null)
prop.SetValue(derivedObj, val);
}
}
return derivedObj;
}

