2013-05-14 16 views
22

Mam klasy, NetworkClient jako klasy bazowej:C#: Konwersja klasy bazowej do dziecka Klasa

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

} 
} 

I mają również klasę dziecka pochodzącego z 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(); 
    } 

} 
} 

Problem polega na tym, gdy próbuję rzucić NetworkClient w SkyfilterClient. Zgłoszony wyjątek, Nie można rzucić obiektu typu "Network.NetworkClient", aby wpisać "Network.SkyfilterClient".

Co jest nie tak z moim kodem? Widzę, że Stream można przekonwertować na NetworkStream, MemoryStream. Dlaczego NetworkClient nie może zostać przekonwertowany na klienta Skyfilter?

Odpowiedz

28

Dopóki obiekt jest rzeczywiście SkyfilterClient, a następnie odlew powinien działać. Oto zmyślony przykład to udowodnić:

using System; 

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

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

Jednakże, jeśli to jest rzeczywiście NetworkClient, to nie można w magiczny sposób sprawiają, że stają się podklasą. Oto przykład:

using System; 

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

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

JEDNAKŻE można utworzyć klasę konwertera. Oto przykład:

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

Należy jednak pamiętać, że istnieje powód, dla którego nie można dokonać konwersji w ten sposób. Być może brakuje kluczowych informacji, których potrzebuje podklasa.

Wreszcie, jeśli chcesz po prostu sprawdzić, czy próba obsada będzie działać, a następnie można użyć is:

if(client is SkyfilterClient) 
    cast 
+0

Witam, próbowałem twojego przykładu z pewną zmianą. I przekonwertował obiekt nadrzędny na obiekt podrzędny - ale nie migrował żadnej właściwości nadrzędnej do obiektu podrzędnego. Dla mojego kodu oczekiwałem metody, która konwertowałaby obiekt nadrzędny na element potomny, a także migrowała właściwości nadrzędne (właściwości podrzędne będą miały wartość NULL). Dowolny pomysł ? –

+0

@HemantTank Najlepszym rozwiązaniem jest uczciwe pytanie w osobnym pytaniu. Nie utrzymuję niektórych z tych starszych pytań atm. –

+1

Dzięki. Na razie używam techniki box/unbox opartej na JSON. Nie idealne, ale działa dobrze dla mojego celu - https://stackoverflow.com/questions/8329470/convert-derived-class-to-base-class –

5

W programie OOP nie można rzutować instancji klasy nadrzędnej na klasę potomną. Możesz tylko rzucić instancję podrzędną w rodzica, z którego dziedziczy.

0

Użyj operatora cast, takie jak:

var skyfilterClient = (SkyfilterClient)networkClient; 
+0

Już to zrobiłem, że wystąpił błąd podczas zrobić Uwierzytelnij ((SkyfilterClient) klient) –

+0

Już to zrobiłem, że wystąpił błąd podczas zrobić uwierzytelnienia ((SkyfilterClient) client) –

+4

W takim przypadku jest prawdopodobne, że twój 'klient' nie jest w rzeczywistości' SkyfilterClient'. –

4

Nie możesz downcast. Jeśli tworzony jest obiekt nadrzędny, nie można go rzutować na dziecko.

Jednym z sugerowanych obejść jest utworzenie interface, które rodzic implementuje. W razie potrzeby dziecko może nadpisać funkcjonalność lub po prostu ujawnić funkcjonalność rodziców. Zmień rzutowanie, aby był interfejsem i wykonaj operacje.

Edit: Może być może również sprawdzić, czy obiekt jest SkyfilterClient użyciu is słowa kluczowego

if(networkClient is SkyfilterClient) 
    { 

    } 
2

można skopiować wartość klasy do klasy Parent Child. Na przykład możesz użyć refleksji, jeśli tak jest.

1

Możesz użyć operatora jako do wykonywania pewnych typów konwersji między zgodnymi typami referencyjnymi lub typami zerowymi.

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

Jestem zaskoczony, że AutoMapper nie znalazł odpowiedzi.

Jak wynika z wszystkich poprzednich odpowiedzi, nie można wykonać typowania. Jednak przy użyciu AutoMapper w kilku wierszach kodu można utworzyć nową instancję SkyfilterClient na podstawie istniejącego NetworkClient.

W istocie, należy umieścić następujące gdzie aktualnie robi swoje rzutowania typów:

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

Oto pełnowymiarową przykład:

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

Oprócz dobrej zdrowej teorii polimorfizmu, bardzo miło jest zauważyć, że w praktyce kod taki jak ten musi być czasem zapisywany. Posiadanie świetnego narzędzia do tego nie tylko oszczędza pracę, ale również zapobiega błędom! –

0

Polecam identyfikacji funkcjonalność trzeba z dowolne podklasy i utworzyć ogólną metodę rzutowania na prawą podklasę.

Miałem ten sam problem, ale naprawdę nie chciałem tworzyć klasy mapowania ani importować biblioteki.

Załóżmy, że potrzebujesz metody "Uwierzytelnij", aby zachować zachowanie z prawej podklasy. W swojej 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); 
} 
Powiązane problemy