2013-05-14 2 views
22

У меня есть класс, NetworkClient в качестве базового класса:C#: Преобразование базового класса для ребенка Класс

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?

ответ

28

Пока объект на самом деле является 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 
+0

Здравствуйте, я попробовал ваш пример с некоторыми изменениями. И он преобразовал родительский объект в дочерний, но он не перенесет какое-либо родительское свойство на дочерний объект. Для моего кода я ожидал метод, который бы преобразовал родительский объект в дочерний, а также перенести родительские свойства (дочерние свойства будут пустыми). Есть идеи ? –

+0

@ GemantTank Лучше всего задать вопрос в отдельном вопросе честно. Я не поддерживал некоторые из этих старых вопросов. –

+1

Спасибо. На данный момент я использую метод Box/unbox на основе JSON. Не идеально, но отлично работает для моей цели - https://stackoverflow.com/questions/8329470/convert-derived-class-to-base-class –

5

В ООП нельзя включить экземпляр родительского класса в дочерний класс. Вы можете только передать дочерний экземпляр родительскому объекту, на который он наследует.

0

Используйте оператор приведения, как таковой:

var skyfilterClient = (SkyfilterClient)networkClient; 
+0

Я уже сделал это, ошибка произошла, когда я Authenticate ((SkyfilterClient) клиент) –

+0

я уже сделал это, ошибка произошла, когда я Authenticate() клиент (SkyfilterClient) –

+4

В этом случае, скорее всего, ваш 'клиент' на самом деле не является« SkyfilterClient ». –

4

Вы не можете downcast. Если родительский объект создан, он не может быть передан ребенку.

Одним из рекомендуемых способов решения проблемы является создание interface, который реализует родитель. Попросите ребенка переопределить функциональность, если это необходимо, или просто выставить функции родителей. Измените приведение, чтобы быть интерфейсом, и выполните операции.

Edit: Может быть также может проверить, если объект является SkyfilterClient с помощью ключевого слова is

if(networkClient is SkyfilterClient) 
    { 

    } 
2

Вы можете скопировать значение родительского класса к классу ребенка. Например, вы можете использовать отражение, если это так.

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 
21

Я удивлен, что 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

В дополнение к хорошей теории здорового полиморфизма, очень приятно видеть некоторое подтверждение, что на практике такой код иногда приходится писать. И наличие отличного инструмента для этого не только экономит работу, но и предотвращает ошибки! –

0

Я рекомендовал бы идентифицировать функциональные возможности вам нужно от любые подклассы и сделать общий метод для включения в правый подкласс.

У меня была такая же проблема, но мне действительно не хотелось создавать класс сопоставления или импортировать библиотеку.

Предположим, вам нужен метод «Аутентификация» для принятия поведения из правого подкласса. В вашем 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); 
} 
Смежные вопросы