2013-05-14 13 views
22

Ho una classe, NetworkClient come classe base:C#: Conversione Classe Base per Bambini Classe

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(); 
    } 

} 
} 

e hanno anche una classe figlia derivata da 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(); 
    } 

} 
} 

Il problema è, che quando sto cercando di lanciare NetworkClient in SkyfilterClient. Viene generata un'eccezione, impossibile eseguire il cast dell'oggetto di tipo 'Network.NetworkClient' per digitare 'Network.SkyfilterClient'.

Cosa c'è di sbagliato nel mio codice? Vedo che Stream può essere convertito in NetworkStream, MemoryStream. Perché NetworkClient non può essere convertito in Skyfilter Client?

risposta

28

Fintanto che l'oggetto è in realtà un SkyfilterClient, poi un cast dovrebbe funzionare. Ecco un esempio inventato per dimostrare questo:

using System; 

class Program 
{ 
    static void Main() 
    { 
     NetworkClient net = new SkyfilterClient(); 
     var sky = (SkyfilterClient)net; 
    } 
} 

public class NetworkClient{} 
public class SkyfilterClient : NetworkClient{} 

Tuttavia, se si è in realtà un NetworkClient, allora non può magicamente rendere diventa la sottoclasse. Ecco un esempio:

using System; 

class Program 
{ 
    static void Main() 
    { 
     NetworkClient net = new NetworkClient(); 
     var sky = (SkyfilterClient)net; 
    } 
} 

public class NetworkClient{} 
public class SkyfilterClient : NetworkClient{} 

TUTTAVIA, è possibile creare una classe di convertitore. Ecco un esempio di questo, anche:

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}; 
    } 
} 

Ma, tieni presente che c'è un motivo per cui non è possibile convertire in questo modo. Potrebbero mancare informazioni chiave di cui la sottoclasse necessita.

Infine, se si desidera solo per vedere se il tentativo di fusione funziona, quindi è possibile utilizzare is:

if(client is SkyfilterClient) 
    cast 
+0

Ciao, ho provato il tuo esempio con qualche cambiamento. E convertì l'oggetto genitore in figlio - ma non migrò alcuna proprietà genitore all'oggetto figlio. Per il mio codice mi aspettavo un metodo che convertisse l'oggetto genitore in figlio e migri anche le proprietà genitore (le proprietà figlio saranno nulle). Qualche idea ? –

+0

@HemantTank La cosa migliore da fare è chiedere in una domanda separata onestamente. Non ho mantenuto alcune di queste domande più vecchie. –

+1

Grazie. Per ora sto usando la tecnica box/unbox basata su JSON. Non perfetto ma funziona bene per il mio scopo - https://stackoverflow.com/questions/8329470/convert-derived-class-to-base-class –

5

In OOP, non è possibile eseguire il cast di un'istanza di una classe genitore in una classe figlio. È possibile eseguire il cast di un'istanza child solo in un genitore da cui eredita.

0

Utilizzare l'operatore di cast, come ad esempio:

var skyfilterClient = (SkyfilterClient)networkClient; 
+0

ho già fatto, si è verificato l'errore quando faccio autenticazione ((SkyfilterClient) client) –

+0

ho già fatto, si è verificato l'errore quando faccio autenticazione (client (SkyfilterClient)) –

+4

In tal caso, è probabile che il tuo 'client' non sia effettivamente un' SkyfilterClient'. –

4

Non puoi downcast. Se l'oggetto genitore è stato creato, non può essere trasmesso al figlio.

Una soluzione suggerita sarebbe quella di creare uno interface che il genitore implementa. Chiedi al bambino di sovrascrivere le funzionalità, se necessario, o semplicemente esporre la funzionalità dei genitori. Cambia il cast come interfaccia e fai le operazioni.

Edit: Può essere potrebbe anche verificare se l'oggetto è un SkyfilterClient utilizzando is parola chiave

if(networkClient is SkyfilterClient) 
    { 

    } 
2

È possibile copiare il valore della classe genitore ad una classe figlia. Ad esempio, potresti usare la riflessione, se così fosse.

1

È possibile utilizzare l'operatore as per eseguire determinati tipi di conversioni tra tipi di riferimento compatibili o tipi nullable.

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

Sono sorpreso AutoMapper non è venuto come risposta.

Come risulta da tutte le risposte precedenti, non è possibile eseguire il typecast. Tuttavia, utilizzando AutoMapper, in alcune righe di codice è possibile creare una nuova SkyfilterClient basata su uno NetworkClient esistente.

In sostanza, si dovrebbe mettere il seguente in cui si sta facendo la vostra fusione di caratteri:

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); 

Ecco un esempio in piena regola:

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

Oltre alla buona teoria del polimorfismo sano, è terribilmente bello vedere qualche riconoscimento che in pratica un codice come questo deve essere scritto a volte. E avere un ottimo strumento per farlo non solo salva il lavoro, ma previene anche i bug! –

0

lo consiglio identificare le funzionalità necessarie da qualsiasi sottoclasse e creare un metodo generico per eseguire il cast nella sottoclasse giusta.

Ho avuto lo stesso problema, ma in realtà non avevo voglia di creare una classe di mappatura o di importare una libreria.

Supponiamo che sia necessario il metodo "Autentica" per eseguire il comportamento dalla sottoclasse destra. Nel vostro 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); 
} 
Problemi correlati