2013-05-14 65 views
22

Tôi có một lớp học, NetworkClient như một lớp cơ sở:C#: Chuyển đổi Base Class để trẻ em Lớp

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

} 
} 

Và cũng có một lớp con có nguồn gốc từ 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(); 
    } 

} 
} 

Vấn đề là, khi tôi cố gắng cast NetworkClient vào SkyfilterClient. Một ngoại lệ được ném, Không thể truyền đối tượng thuộc loại 'Network.NetworkClient' để nhập 'Network.SkyfilterClient'.

Có vấn đề gì với mã của tôi? Tôi thấy rằng Stream có thể được chuyển đổi thành NetworkStream, MemoryStream. Tại sao NetworkClient không thể chuyển đổi thành Skyfilter Client?

Trả lời

28

Chừng nào các đối tượng thực sự là một SkyfilterClient, sau đó một dàn diễn viên nên làm việc. Dưới đây là một ví dụ để chứng minh giả tạo này:

using System; 

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

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

Tuy nhiên, nếu nó thực sự là một NetworkClient, thì bạn không thể kỳ diệu làm cho nó trở thành phân lớp. Dưới đây là ví dụ về điều đó:

using System; 

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

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

BAO GIỜ, bạn có thể tạo lớp chuyển đổi. Dưới đây là ví dụ về điều đó, cũng:

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

Nhưng, hãy nhớ rằng có lý do bạn không thể chuyển đổi theo cách này. Bạn có thể thiếu thông tin quan trọng mà lớp con cần.

Cuối cùng, nếu bạn chỉ muốn xem các diễn viên đã cố gắng sẽ làm việc, sau đó bạn có thể sử dụng is:

if(client is SkyfilterClient) 
    cast 
+0

Xin chào, tôi đã thử ví dụ của bạn với một số thay đổi. Và nó đã chuyển đổi đối tượng cha mẹ thành con - nhưng nó không di chuyển bất kỳ thuộc tính cha mẹ nào đến đối tượng con. Đối với mã của tôi, tôi đã mong đợi một phương thức mà có thể chuyển đổi đối tượng cha thành con và cũng di chuyển các thuộc tính cha (các thuộc tính con sẽ là null). Bất kỳ ý tưởng ? –

+0

@HemantTank Đặt cược tốt nhất của bạn là hỏi một câu hỏi riêng biệt một cách trung thực. Tôi đã không được duy trì một số trong những câu hỏi cũ atm. –

+1

Cảm ơn. Bây giờ tôi đang sử dụng kỹ thuật hộp/unbox dựa trên JSON. Không hoàn hảo nhưng hoạt động tốt cho mục đích của tôi - https://stackoverflow.com/questions/8329470/convert-derived-class-to-base-class –

5

Trong OOP, bạn không thể truyền một thể hiện của một lớp cha vào một lớp con. Bạn chỉ có thể truyền một thể hiện con vào một phụ huynh mà nó kế thừa từ đó.

0

Sử dụng các nhà điều hành dàn diễn viên, như vậy:

var skyfilterClient = (SkyfilterClient)networkClient; 
+0

Tôi đã làm điều đó, các lỗi xảy ra khi tôi làm Authenticate ((SkyfilterClient) khách hàng) –

+0

Tôi đã làm điều đó, các lỗi xảy ra khi tôi làm Authenticate ((SkyfilterClient) khách hàng) –

+4

Trong trường hợp đó, có khả năng là 'client' của bạn không thực sự là' SkyfilterClient'. –

4

Bạn không thể downcast. Nếu đối tượng cha được tạo, nó không thể được truyền cho đứa trẻ.

Một giải pháp được đề xuất là Tạo một interface mà phụ huynh thực hiện. Có chức năng ghi đè trẻ em nếu cần hoặc chỉ hiển thị chức năng của cha mẹ. Thay đổi dàn diễn viên thành giao diện và thực hiện các thao tác.

Edit: Có thể cũng có thể kiểm tra xem đối tượng là một SkyfilterClient sử dụng is từ khóa

if(networkClient is SkyfilterClient) 
    { 

    } 
2

Bạn có thể sao chép giá trị của tầng lớp phụ huynh về một lớp trẻ. Ví dụ, bạn có thể sử dụng sự phản chiếu nếu đúng như vậy.

1

Bạn có thể sử dụng toán tử as để thực hiện một số loại chuyển đổi nhất định giữa các loại tham chiếu tương thích hoặc các loại có thể vô hiệu.

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

Tôi ngạc nhiên khi AutoMapper chưa xuất hiện dưới dạng câu trả lời.

Như đã giải thích rõ ràng từ tất cả các câu trả lời trước, bạn không thể thực hiện thao tác gõ. Tuy nhiên, bằng cách sử dụng AutoMapper, trong một vài dòng mã, bạn có thể có một SkyfilterClient được khởi tạo mới dựa trên NetworkClient hiện có.

Về bản chất, bạn sẽ đặt như sau nơi bạn đang làm typecasting của bạn:

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

Dưới đây là một full-blown dụ:

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

Ngoài lý thuyết đa hình tốt lành mạnh, thật tuyệt vời khi thấy một số thừa nhận rằng trong mã thực hành như thế này phải được viết đôi khi. Và có một công cụ tuyệt vời để làm điều đó không chỉ tiết kiệm công việc mà còn ngăn ngừa lỗi! –

0

Tôi muốn giới thiệu việc xác định các chức năng bạn cần từ bất kỳ lớp con nào và tạo một phương thức chung để đúc thành lớp con bên phải.

Tôi đã gặp vấn đề tương tự, nhưng thực sự không cảm thấy muốn tạo một số lớp ánh xạ hoặc nhập thư viện.

Giả sử bạn cần phương thức 'Authenticate' để thực hiện hành vi từ lớp con bên phải. Trong NetworkClient của bạn:

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); 
} 
Các vấn đề liên quan