2010-12-20 38 views
6

Có không nhiều giải thích cho điều này, đây là những gì tôi có:Struct trong Struct, có khả năng thay đổi bên trong cấu trúc kiểu

public struct PACKET_HEADER 
    { 
     public string computerIp; 
     public string computerName; 
     public string computerCustomName; 
    }; 

    public struct PACKET 
    { 
     public PACKET_HEADER pktHdr; 
     public PACKET_DATA pktData; 
    }; 


    public struct PACKET_DATA 
    { 
     public Command command; 
     public string data; 
    }; 

    public struct DATA_MESSAGE 
    { 
     public string message; 
    }; 

    public struct DATA_FILE 
    { 
     public string fileName; 
     public long fileSize;  
    }; 

Về cơ bản tôi muốn các trường dữ liệu trong PACKET_DATA để có thể là một trong hai DATA_FILE hoặc DATA_MESSAGE. Tôi biết loại cần phải được thay đổi nhưng tôi không biết phải làm gì, là một lựa chọn generics?

kết quả cuối cùng nên để tôi có thể làm một trong hai:

pktData.data.fileName hoặc pktData.data.message

EDIT

tôi có thể làm:

public struct PACKET_DATA 
{ 
    public Command command; 
    public string data; 
    public DATA_MESSAGE data_message; 
    public DATA_FILE data_file; 
}; 

và chỉ đặt data_message hoặc tệp thành rỗng khi tôi từng làm không cần chúng? điều này sẽ ảnh hưởng như thế nào đến mảng serial/byte và dữ liệu được gửi đi. Nếu tôi sử dụng các lớp học tôi sẽ không có cùng một vấn đề

EDIT 2

public struct PACKET_MESSAGE 
{ 
    public PACKET_HEADER pktHdr; 
    public Command command; 
    public DATA_MESSAGE pktData; 
}; 

public struct PACKET_FILE 
{ 
    public PACKET_HEADER pktHdr; 
    public Command command; 
    public DATA_FILE pktData; 
}; 

Sửa 3

Tôi có một tiệt trùng và khử tiệt trùng làm việc với ví dụ ban đầu của tôi, nếu không có trục trặc với điều đó sau đó serialization thực tế được thực hiện.

EDIT 4

tất cả mọi thứ dường như được làm việc, ngoài một điều serializer của tôi là nhận được "cố gắng để đọc hay ghi bộ nhớ được bảo vệ. Đây thường là một dấu hiệu cho thấy bộ nhớ khác là tham nhũng." gunna có một cái nhìn vào nó khi bài giải pháp làm việc của tôi :)

EDIT 5

public static byte[] Serialize(object anything) 
    { 
     int rawsize = Marshal.SizeOf(anything); 
     byte[] rawdatas = new byte[rawsize]; 
     GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned); 
     IntPtr buffer = handle.AddrOfPinnedObject(); 
     Marshal.StructureToPtr(anything, buffer, false); 
     handle.Free(); 
     return rawdatas; 
    } 

    public static object Deserialize(byte[] rawdatas, Type anytype) 
    { 
     int rawsize = Marshal.SizeOf(anytype); 
     if (rawsize > rawdatas.Length) 
      return null; 
     GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned); 
     IntPtr buffer = handle.AddrOfPinnedObject(); 
     object retobj = Marshal.PtrToStructure(buffer, anytype); 
     handle.Free(); 
     return retobj; 
    } 

CUỐI CÙNG

Các cấu trúc:

public struct PACKET_HEADER 
{ 
    public string computerIp; 
    public string computerName; 
    public string computerCustomName; 
}; 

public struct PACKET 
{ 
    public PACKET_HEADER pktHdr; 
    public PACKET_DATA pktData; 
}; 

public struct PACKET_DATA 
{ 
    public Command command; 
    public IDATA data; 
    public T GetData<T>() where T : IDATA 
    { 
     return (T)(data); 
    } 
} 

public interface IDATA { } 

public struct DATA_MESSAGE : IDATA 
{ 
    public string message; 
} 

public struct DATA_FILE : IDATA 
{ 
    public string fileName; 
    public long fileSize; 
} 

Làm thế nào để tạo ra một mới Gói (probally có thể kết hợp với nhau tbh):

public static PACKET CreatePacket(Command command) 
    { 
     PACKET packet; 
     packet.pktHdr.computerIp = Settings.ComputerIP; 
     packet.pktHdr.computerName = Settings.ComputerName; 
     packet.pktHdr.computerCustomName = Settings.ComputerCustomName; 

     packet.pktData.command = command; 
     packet.pktData.data = null; 

     return packet; 
    } 

    public static PACKET CreatePacket(Command command, DATA_MESSAGE data_message) 
    { 
     PACKET packet; 
     packet.pktHdr.computerIp = Settings.ComputerIP; 
     packet.pktHdr.computerName = Settings.ComputerName; 
     packet.pktHdr.computerCustomName = Settings.ComputerCustomName; 

     packet.pktData.command = command; 
     packet.pktData.data = data_message; 

     return packet; 
    } 

    public static PACKET CreatePacket(Command command, DATA_FILE data_file) 
    { 
     PACKET packet; 
     packet.pktHdr.computerIp = Settings.ComputerIP; 
     packet.pktHdr.computerName = Settings.ComputerName; 
     packet.pktHdr.computerCustomName = Settings.ComputerCustomName; 

     packet.pktData.command = command; 
     packet.pktData.data = data_file; 

     return packet; 
    } 

(de) tuần tự hóa ở trên.

ví dụ đơn giản:

PACKET packet = Packet.CreatePacket(command, data_file); 
byte[] byData = Packet.Serialize(packet); 

đầu kia:

PACKET returnPacket = (PACKET)Packet.Deserialize(socketData.dataBuffer, typeof(PACKET)); 
         // Get file 
string fileName = returnPacket.pktData.GetData<DATA_FILE>().fileName; 
long fileSize = returnPacket.pktData.GetData<DATA_FILE>().fileSize; 

Tất cả dường như được làm việc tốt đẹp và dandy :)

+0

chỉ cần làm rõ về EDIT của bạn: cấu trúc, là loại giá trị, không bao giờ là rỗng. Bạn sẽ cần một lá cờ cho biết thành viên nào là hợp lệ. –

+0

các bạn chỉ nhận ra rằng chúng không thể rỗng. im nghĩ đến việc có 2 loại gói, xem ở trên, điều này có ý nghĩa hơn không? – Metalstorm

+0

Ok, nhưng hãy nhớ rằng mã mà deserializes sẽ cần phải xác định loại. Tôi thực sự thích hai phiên bản cấu trúc con của bạn, bạn chỉ cần một 'bool isMessagePacket;'. –

Trả lời

1
public struct PACKET_DATA 
    { 
     public IData data; 
     public T GetData<T>() where T : IDATA 
     { 
      return (T)data; 
     } 
    } 

    public interface IDATA { } 

    public struct DATA_MESSAGE : IDATA 
    { 
     public string message; 
    } 

    public struct DATA_FILE : IDATA 
    { 
     public string fileName; 
     public long fileSize; 
    } 

PACKET_DATA packetData = new PACKET_DATA(); 
packetData.data = new DATA_MESSAGE(); 
var message = packetData.GetData<DATA_MESSAGE>().message; 
+0

cấu trúc công khai PACKET { công khai PACKET_HEADER pktHdr; công khai PACKET_DATA <> pktData; }; không phải là vấn đề như trước đây? những gì đi giữa <> nếu T của nó sau đó cấu trúc PACKET cũng sẽ phải mất một T? làm cho nó lộn xộn – Metalstorm

+0

@Metalstorm: Xem chỉnh sửa của tôi. –

+0

ive đã cố gắng thực hiện điều này, nó trông tất cả tốt ngoài T trong "trở lại T (dữ liệu);" nó trở lại với lỗi 'T' là 'tham số kiểu' nhưng được sử dụng như một 'biến' – Metalstorm

0

Bạn có thể làm điều đó với Generics, nhưng sau đó một tham số kiểu sẽ lan truyền đến cấu trúc PACKET mà tôi đoán sẽ làm cho nó khó xử khi làm việc và không phải là thứ bạn muốn.

Mục đích của việc sử dụng cấu trúc ở đây, thay vì các lớp học là gì? Là nó cho interop? (Trong trường hợp đó, kịch bản interop sẽ ra lệnh cho giải pháp đúng). Hoặc là nó để tránh phân bổ quyền anh/heap?

+0

"Bạn có thể làm điều đó với Generics, nhưng sau đó một tham số kiểu sẽ tuyên truyền để cấu trúc PACKET mà tôi đoán sẽ làm cho nó vụng về để làm việc với và không được những gì bạn muốn. " Tôi đã có một cái nhìn tại đó nhưng thực sự nó nhận được rất lộn xộn. Cấu trúc gói (và nội dung của nó) được tuần tự hóa và gửi qua mạng như một mảng byte. Mục đích chỉ là để lưu trữ một lượng thông tin nhỏ trong một khoảng thời gian ngắn, tôi đọc rằng cấu trúc tốt hơn cho điều này chứ không phải là các lớp. – Metalstorm

+0

Chủ yếu là bạn muốn tuần tự hóa hiệu quả? Nếu vậy, cách tiếp cận tối ưu là viết một serializer tùy chỉnh. Chi phí của việc tạo ra các loại lớp này thay vì cấu trúc (về mặt GC) không lớn. –

+0

đã thêm chỉnh sửa cho câu hỏi gốc – Metalstorm

0

Làm thế nào để xác định giao diện không có thật mà cả hai cấu trúc kế thừa? Tất nhiên điều này sẽ không giải quyết vấn đề serialization của bạn, nhưng như đã nói, bạn có thể cần một phương pháp serialization tùy chỉnh anyway.

public interface IDataType 
{ 
} 

public struct PACKET_DATA 
{ 
    public Command command; 
    public IDataType data; 
}; 

public struct DATA_MESSAGE : IDataType 
{ 
    public string message; 
}; 

public struct DATA_FILE : IDataType 
{ 
    public string fileName; 
    public long fileSize;  
}; 
+0

điều này có vẻ tốt đẹp, tuy nhiên khi tôi thử gói GÓI; packet.pktData.data. Không có gì xuất hiện ngoài tiêu chuẩn ToString vv – Metalstorm

3

Câu hỏi này cần một câu trả lời rõ ràng, vì vậy tôi sẽ cố gắng tổng hợp:

Nếu bạn muốn để có một cấu trúc dữ liệu C# và chuyển đổi nó thành một mảng byte, bạn có thể làm điều đó với cấu trúc và Marshaling, hoặc với các lớp (hoặc cấu trúc, nhưng tại sao bạn) và một khung tuần tự hóa (như BinaryFormatter), hoặc logic tuần tự hóa tùy chỉnh (như với BinaryWriter). Chúng ta có thể có một cuộc tranh luận về cái nào tốt hơn, nhưng giả sử bây giờ chúng ta đang đi với các cấu trúc, và chúng ta đang sử dụng Marshaling. Mặc dù tôi sẽ nói, các cấu trúc đó rất hạn chế, và nên được sử dụng chủ yếu khi cần thiết cho interop với các hàm API của Win32.

Vì vậy, vấn đề là, chúng tôi có cấu trúc vùng chứa, có thể chứa một trong hai loại cấu trúc con. Nếu bạn đang đi đến Marshal một cấu trúc, những thứ như generics và hoặc sử dụng một giao diện chung cho các loại con struct của bạn sẽ không bay. Về cơ bản bạn chỉ có tùy chọn là để có container có cả hai cấu trúc và cờ bool cho biết cấu trúc nào sẽ được sử dụng. Điều này có nhược điểm của việc tăng kích thước của các gói tin của bạn bởi vì bạn đang gửi cấu trúc con chưa sử dụng là tốt.

Trong trường hợp trong tầm tay, kết quả sẽ như thế này:

public struct PACKET_DATA 
{ 
    public Command command; 
    public string data; 
    public bool is_message_packet; 
    public DATA_MESSAGE data_message; 
    public DATA_FILE data_file; 
}; 

Điều đó nói rằng, trong trường hợp bạn sử dụng cấu trúc và Marshalling chỉ thực sự sẽ làm việc trong quá trình của riêng bạn, bởi vì cấu trúc của bạn có chứa Strings. Khi một struct chứa các con trỏ tới các chuỗi không cố định, các chuỗi đó được phân bổ ở nơi khác và sẽ không là một phần của mảng byte mà bạn sao chép, chỉ các con trỏ tới chúng sẽ là. Bạn cũng cần phải gọi Marshal.DestroyStructure tại một số điểm, với IntPtr bạn đã chuyển đến StructureToPtr, để làm sạch các tài nguyên chuỗi này.

Vì vậy, đạo đức của câu chuyện: bạn có thể tạo cấu trúc làm những gì bạn đã hỏi ban đầu: có. Nếu bạn sử dụng chúng như bạn là: không. Bởi vì bạn có cấu trúc dữ liệu có kích thước biến mà bạn đang cố gắng gửi qua mạng (tôi đoán vì cấu trúc được gọi là GÓI), cấu trúc sẽ không hoạt động, bạn thực sự cần sử dụng một số loại khung tuần tự hóa hoặc logic tuần tự tùy chỉnh.

+0

Hãy nhìn vào bản chỉnh sửa cuối cùng của tôi, tất cả dường như đang hoạt động tốt – Metalstorm

+0

Tôi nghĩ thực tế là "làm việc" là một điều tưởng tượng bởi vì bạn đang sử dụng mã thử nghiệm trong một quá trình . Hãy lấy mã đó, và thử gửi dữ liệu qua một ổ cắm mạng, và tôi gần như chắc chắn nó sẽ thất bại. Điều này là do các chuỗi cấu trúc của bạn chứa không được lưu trữ với cấu trúc, cấu trúc nguyên soái chỉ có các con trỏ tới chúng, chúng được phân bổ ở nơi khác. –

+0

Ngoài ra, khi một cấu trúc chứa một thành viên giao diện, nó xử lý thành viên đó làm tham chiếu. Nói cách khác, cấu trúc cha có chứa một con trỏ, thay vì chứa cấu trúc con inline. Điều này chắc chắn sẽ không bay. –

Các vấn đề liên quan