Tôi đang cố gắng tìm một mẫu thiết kế đẹp, sạch hoặc thường được chấp nhận để xử lý một kiểu liệt kê loại cá nhân chỉ được biết khi chạy.Mẫu thiết kế hoặc các giải pháp được chấp nhận để tránh chuyển đổi trên các loại
Tôi biết câu hỏi tương tự đã được hỏi trước đó, nhưng vẫn không rõ ràng với tôi rằng các triển khai thay thế có lợi thế đáng kể so với một công tắc hoặc một loạt các lỗi.
Trước tiên, tôi sẽ trình bày một vài triển khai, sau đó tôi sẽ đặt câu hỏi: Các triển khai này có tốt hơn hoặc được ưu tiên hơn chuyển đổi đơn giản không? Nếu vậy, tại sao? Nếu không, tai sao không?
Trong đơn đăng ký của tôi, tôi gửi và nhận dữ liệu qua luồng. Vào thời gian chạy, tôi nhận được một cấu trúc dữ liệu qua serialization mô tả những trường nào được đặt trong dữ liệu nhị phân của tôi. Điều này bao gồm Loại dữ liệu trong trường, tức là Int32, Bool, Double, vv Tại thời điểm thiết kế, tất cả những gì tôi biết là dữ liệu có thể thuộc một trong nhiều loại. Tôi cần phải đọc các trường từ luồng và xử lý dữ liệu một cách thích hợp.
Nếu chuyển trên các loại được phép, một giải pháp có thể như sau:
Non-Working Code:
object ReadDataField(byte [] buff, ref int position,
Dictionary<int, Type> fields)
{
object value;
int field = buff[position];
position++;
switch(fields[field])
{
case typeof(Int32):
{
value = (Int32)BitConverter.ToInt32(buff, position);
position += sizeof(Int32);
break;
}
case typeof(Int16):
{
value = (Int16)BitConverter.ToInt16(buff, position);
position += sizeof(Int16);
break;
}
// Etc...
}
return value;
}
Theo tôi, mã này có lợi thế là đơn giản , dễ đọc và dễ bảo trì.
Tuy nhiên, khi chuyển đổi trên các loại không có sẵn trong C# Tôi thực hiện ở trên như sau:
Mã làm việc:
enum RawDataTypes
{
Int32,
Int16,
Double,
Single,
etc.
}
object ReadDataField(byte [] buff, ref int position,
Dictionary<int, RawDataTypes> fields)
{
object value;
int field = buff[position];
position++;
switch(fields[field])
{
case RawDataTypes.Int32:
{
value = (int)BitConverter.ToInt32(buff, position);
position += sizeof(int);
break;
}
case RawDataTypes.Int16:
{
value = (Int16)BitConverter.ToInt16(buff, position);
position += sizeof(Int16);
break;
}
// Etc.
}
return value;
}
Đây rõ ràng là một công việc xung quanh, nhưng nó cũng là đơn giản và dễ bảo trì.
Tuy nhiên, có một số bài viết chi tiết về chuyển đổi trên các loại không có sẵn trong C#. Và bên cạnh những khó khăn đối phó với thừa kế theo cách mà mang lại một kết quả mong đợi, vv, tôi đã nhìn thấy nhiều câu trả lời đã nói có một cách tiếp cận "tốt hơn" nhiều hơn phù hợp với tinh thần của lập trình hướng đối tượng.
Các giải pháp phổ biến được đề xuất là 1) sử dụng đa hình hoặc 2) sử dụng tra cứu từ điển. Nhưng việc thực hiện hoặc có những thách thức riêng.
Về đa hình, sau đây là một ví dụ về "nó sẽ không được tốt đẹp nếu nó làm việc" mã:
Non-Working Thực hiện Polymorphism:
object ReadDataField(byte [] buff, int position,
Dictionary<int, Type> fields)
{
int field = buff[position];
position++;
object value = Activator.CreateInstance(fields[field]);
// Here we're trying to use an extension method on the raw data type.
value.ReadRawData(buff, ref position);
return value;
}
public static Int32 ReadRawData(this Int32 value, byte[] buff, ref int position)
{
value = BitConverter.ToInt32(buff, position);
position += sizeof(Int32);
return value;
}
public static Int16 ReadRawData(this Int16 value, byte[] buff, ref int position)
{
value = BitConverter.ToInt16 (buff, position);
position += sizeof(Int16);
return value;
}
// Additional methods for each type...
Nếu bạn cố gắng biên dịch mã ở trên bạn sẽ nhận được:
'đối tượng' không chứa định nghĩa cho 'ReadRawData' và quá tải phương pháp mở rộng tốt nhất 'RawDataFieldExt ensions.ReadRawData (ngắn, byte [], ref int) 'có một số đối số không hợp lệ trong blah blah ...
Bạn không thể phân lớp các loại dữ liệu thô để thêm chức năng, bởi vì chúng được niêm phong, vì vậy các phương pháp mở rộng có vẻ như một tùy chọn. Tuy nhiên, các phương thức mở rộng sẽ không chuyển đổi từ 'object' sang kiểu thực tế, mặc dù gọi value.GetType() trả về kiểu cơ bản: System.Int32, System.Int16, etc. Sử dụng từ khóa 'dynamic' không trợ giúp, vì, vì you can't use extension methods on a dynamic type.
Trên đây thể được thực hiện để làm việc bằng cách thông qua một thể hiện của đối tượng chính nó như là một tham số để phương pháp với các thông số đa hình:
Thực hiện làm việc của Polymorphism:
object ReadDataField(byte [] buff, int position,
Dictionary<int, Type> fields)
{
int field = buff[position];
position++;
dynamic value = Activator.CreateInstance(fields[field]);
// Here the object is passed to an overloaded method.
value = ReadRawData(value, buff, ref position);
return value;
}
public static Int32 ReadRawData(Int32 value, byte[] buff, ref int position)
{
value = BitConverter.ToInt32(buff, position);
position += sizeof(Int32);
return value;
}
public static Int16 ReadRawData(Int16 value, byte[] buff, ref int position)
{
value = BitConverter.ToInt16 (buff, position);
position += sizeof(Int16);
return value;
}
// Additional methods for each type...
Trên đây mã hoạt động và vẫn còn đơn giản và có thể duy trì, và có lẽ nhiều hơn "theo tinh thần lập trình hướng đối tượng."
Nhưng thực sự có "tốt hơn không?" Tôi sẽ cho rằng nó làm cho nó thêm nhiều hơn khó duy trì, vì nó đòi hỏi nhiều tìm kiếm hơn để xem loại nào đã được triển khai.
Phương pháp thay thế là sử dụng tra cứu từ điển. mã như vậy có thể trông như thế này:
điển Thực hiện:
delegate object ReadDelegate(byte [] buff, ref int position);
static Dictionary<Type, ReadDelegate> readers = new Dictionary<Type, ReadDelegate>
{
{ typeof(Int32), ReadInt32 },
{ typeof(Int16), ReadInt16 },
// Etc...
};
object ReadDataField(byte [] buff, int position,
Dictionary<int, Type> fields)
{
int field = buff[position];
position++;
object value = readers[fields[field]](buff, ref position);
return value;
}
public static object ReadInt32(byte[] buff, ref int position)
{
Int32 value = BitConverter.ToInt32(buff, position);
position += sizeof(Int32);
return value;
}
public static object ReadInt16(byte[] buff, ref int position)
{
return BitConverter.ToInt16(buff, position);
position += sizeof(Int16);
return value;
}
// Additional methods for each type...
Một lợi thế của việc thực hiện từ điển, theo ý kiến của tôi, so với các giải pháp đa hình là nó liệt kê tất cả các loại có thể được xử lý ở một nơi dễ đọc. Điều này rất hữu ích cho việc bảo trì.
Tuy nhiên, với những ví dụ này, có triển khai nào tốt hơn, sạch hơn, được chấp nhận hơn, v.v. có lợi thế đáng kể so với trên không? Các triển khai này có sử dụng tính đa hình hoặc tra cứu từ điển được ưu tiên hơn bằng cách sử dụng nút chuyển không? Tôi không thực sự tiết kiệm bất kỳ mã nào, và tôi không chắc rằng tôi đã tăng khả năng bảo trì của mã.
Trong mọi trường hợp, tôi vẫn cần phải liệt kê từng loại với phương pháp riêng của nó. Đa hình là trì hoãn điều kiện cho chính ngôn ngữ đó, chứ không phải là rõ ràng với một công tắc hoặc nếu-thì. Sử dụng một từ điển là dựa vào các điều kiện nội bộ để thực hiện tra cứu riêng của nó. Vào cuối ngày, sự khác biệt là gì?
Hút thuốc lá, đó là câu hỏi được viết rộng rãi. Có, đa hình là cách thông thường để tránh phải bật các loại, kiểm tra các loại cụ thể, v.v. Vui lòng thu hẹp câu hỏi để có thể trả lời một cách cụ thể, rõ ràng. Xem http://stackoverflow.com/help/how-to-ask –
Tôi thích câu hỏi được kể lại như những câu chuyện :) Nó trình bày toàn bộ quá trình suy nghĩ và dẫn dắt tất cả các khả năng. Chỉ là suy nghĩ của tôi. – Fka
@PeterDuniho, tôi viết lại câu hỏi của mình để thu hẹp nó xuống một chút. Tuy nhiên, tôi muốn kết hợp một số ví dụ triển khai và suy nghĩ đằng sau chúng. – tfjield