Подтвердить что ты не робот

С#: преобразование базового класса в дочерний класс

У меня есть класс 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?

4b9b3361

Ответ 1

Пока объект фактически является 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

Ответ 2

Я удивлен, что 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 NumWheels and HasMotor properties which exist due to inheritance, are populated by AutoMapper.
        cars.Add(car);
      }
      return cars;
    }
  }

Ответ 3

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

Ответ 4

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

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

Изменить: Возможно, также можно проверить, является ли объект SkyfilterClient ключевым словом is

   if(networkClient is SkyfilterClient)
   {

   }

Ответ 5

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

Ответ 6

Вы можете использовать оператор 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

Ответ 7

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

var skyfilterClient = (SkyfilterClient)networkClient;

Ответ 8

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

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

Скажем, вам нужен метод "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);
}