68

クラス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 に変換できないのはなぜですか?

4

11 に答える 11

74

オブジェクトが実際に である限り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
于 2013-05-14T02:47:07.477 に答える
46

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;
    }
  }
于 2014-09-03T21:29:29.567 に答える
4

できませんdowncast。親オブジェクトが作成されると、子にキャストできません。

interface推奨される回避策の 1 つは、親が実装する を作成することです。必要に応じて子にオーバーライド機能を持たせるか、単に親の機能を公開します。キャストをインターフェイスに変更し、操作を行います。

編集:オブジェクトがSkyfilterClientusingisキーワードであるかどうかを確認することもできます

   if(networkClient is SkyfilterClient)
   {

   }
于 2013-05-14T02:44:10.397 に答える