2011-08-07 27 views
14

Tôi đang đùa giỡn với ý tưởng tạo các kiểu giá trị .NET nguyên thủy hơn loại an toàn hơn và nhiều hơn nữa "tự tạo tài liệu" bằng cách gói chúng trong tùy chỉnh struct s. Tuy nhiên, tôi tự hỏi nếu nó thực sự có giá trị nỗ lực trong phần mềm thế giới thực.Loại giá trị .NET nguyên thủy chống kiểu thông qua các cấu trúc tùy chỉnh: Có đáng để nỗ lực không?

(Đó là "nỗ lực" có thể được nhìn thấy dưới đây:. Phải áp dụng các mẫu mã tương tự một lần nữa và một lần nữa Chúng tôi tuyên bố struct s và do đó không thể sử dụng thừa kế để loại bỏ mã lặp lại, và kể từ khi các nhà khai thác quá tải phải được khai báo static, họ phải được xác định đối với từng loại riêng biệt)

Đi này (phải thừa nhận là tầm thường) ví dụ:.

struct Area 
{ 
    public static implicit operator Area(double x) { return new Area(x); } 
    public static implicit operator double(Area area) { return area.x; } 

    private Area(double x) { this.x = x; } 
    private readonly double x; 
} 

struct Length 
{ 
    public static implicit operator Length(double x) { return new Length(x); } 
    public static implicit operator double(Length length) { return length.x; } 

    private Length(double x) { this.x = x; } 
    private readonly double x; 
} 

Cả hai AreaLength về cơ bản là double, nhưng làm tăng thêm với ý nghĩa cụ thể. Nếu bạn đã xác định một phương thức như & hellip;

Area CalculateAreaOfRectangleWith(Length width, Length height) 

& hellip; không may không thể trực tiếp vượt qua trong tình huống Area. Càng xa càng tốt.

NHƯNG: Bạn có thể dễ dàng tránh né rõ ràng cải thiện an toàn kiểu này đơn giản bằng cách đúc một Area-double, hoặc bằng cách tạm thời lưu trữ một Area trong một biến double, và sau đó đi qua đó vào phương pháp mà một Length dự kiến:

Area a = 10.0; 

    double aWithEvilPowers = a; 
    … = CalculateAreaOfRectangleWith((double)a, aWithEvilPowers); 

Câu hỏi: có ai ở đây có kinh nghiệm với việc sử dụng rộng rãi của tùy chỉnh như struct loại trong thực tế/phần mềm sản xuất? Nếu vậy:

  • đã bọc của kiểu giá trị nguyên thủy trong tùy chỉnh struct s bao giờ trực tiếp dẫn đến lỗi ít hơn, hoặc trong mã dễ bảo trì hơn, hoặc đưa ra bất kỳ lợi thế lớn khác (s)?

  • Hoặc các lợi ích của tùy chỉnh struct là quá nhỏ để chúng có thể được sử dụng trong thực tế?


P.S .: Khoảng 5 năm đã trôi qua kể từ khi tôi hỏi câu hỏi này. Tôi đang đăng some of my experiences that I've made since then làm câu trả lời riêng.

+1

** P.S.: ** Tôi biết rằng người ta có thể loại bỏ một trong hai kỹ thuật sideste được mô tả bằng cách xác định toán tử 'Area -> double' thay vì' implicit'. – stakx

+0

Tôi có lẽ không nghĩ đủ lớn, nhưng bạn có thấy rằng loại lỗi này xuất hiện rất thường xuyên cho bạn? Đó là - một khi bạn có dữ liệu của bạn từ nguồn bên ngoài (có lẽ không được phân loại) và vào chương trình của bạn, nó sẽ ở trong một cấu trúc nào đó mà nó được truy cập bởi một số tên có ý nghĩa như "object.length". Bạn thường không kết thúc việc đi qua những con số thô, mà đúng hơn là bạn đi qua các cấu trúc. Tôi nghĩ loại lỗi này rất hiếm, tôi không thể nghĩ đến nó. –

+0

Tôi nghi ngờ loại lỗi này không xảy ra thường xuyên với tôi, @jamietre, nhưng tôi chưa có bất kỳ thống kê cứng nào để hỗ trợ hoặc bác bỏ giả định đó. Tuy nhiên, có những lợi ích tiềm năng khác; mã dễ hiểu hơn có thể là một trong số đó. Tôi chỉ không chắc chắn lên phía trước nếu điều đó biện minh cho việc phải thực hiện rất nhiều loại 'struct' tương tự nhau ... – stakx

Trả lời

11

Tôi đã làm điều này trong một dự án cách đây vài năm, với một số kết quả khác nhau.Trong trường hợp của tôi, nó đã giúp rất nhiều việc theo dõi các loại ID khác nhau và có lỗi thời gian biên dịch khi sử dụng sai loại ID. Và tôi có thể nhớ lại một số trường hợp nó ngăn chặn những lỗi thực sự gây ra. Vì vậy, đó là mặt cộng. Về mặt tiêu cực, nó không phải là rất trực quan cho các nhà phát triển khác - cách tiếp cận này không phải là rất phổ biến, và tôi nghĩ rằng các nhà phát triển khác đã nhầm lẫn với tất cả các loại mới này. Ngoài ra, tôi dường như nhớ lại chúng tôi đã có một số vấn đề với serialization, nhưng tôi không thể nhớ các chi tiết (xin lỗi).

Vì vậy, nếu bạn đang đi để đi tuyến đường này, tôi muốn giới thiệu một vài điều:

1) Hãy chắc chắn rằng bạn nói chuyện với các người dùng khác trên nhóm của bạn đầu tiên, giải thích những gì bạn đang cố gắng để hoàn thành và xem bạn có thể nhận được "mua vào" từ mọi người không. Nếu mọi người không hiểu giá trị, bạn sẽ liên tục chiến đấu chống lại tâm lý của "điểm của tất cả mã bổ sung này" là gì?

2) Xem xét tạo mã boilerplate của bạn bằng cách sử dụng công cụ như T4. Nó sẽ làm cho việc bảo trì mã dễ dàng hơn nhiều. Trong trường hợp của tôi, chúng tôi đã có khoảng một tá các loại này và đi theo con đường tạo mã tạo ra những thay đổi dễ dàng hơn và ít bị lỗi hơn nhiều.

Đó là kinh nghiệm của tôi. Chúc may mắn!

John

+0

Âm thanh như một cách tiếp cận rất hợp lý. +1 – stakx

+0

+1 để sử dụng T4 - Nó đã lưu rất nhiều công việc cho chúng tôi. Tuyệt vời cho việc dập tắt các loại cho Id và tương tự. Và như John đã nói, giúp ngăn ngừa lỗi. – Will

3

Đây là một bước tiếp theo logic để Notation Hungary, xem một bài báo tuyệt vời từ Joel đây http://www.joelonsoftware.com/articles/Wrong.html

Chúng tôi đã làm một cái gì đó tương tự như trong một dự án/API nơi đặc biệt. các nhà phát triển khác cần sử dụng một số giao diện của chúng tôi và nó ít có "trường hợp hỗ trợ báo động sai" hơn - vì nó thực sự rõ ràng những gì được cho phép/cần thiết ... Tôi nghi ngờ điều này có nghĩa là ít lỗi hơn. các ứng dụng kết quả không phải là của chúng tôi ...

+0

+1, tôi đã đọc bài viết đó hơn một năm trước, nhưng sẽ không nhớ nó trong ngữ cảnh câu hỏi của tôi. Thú vị khi nghĩ đến ký hiệu Hungary theo cách này. Cảm ơn! – stakx

1

Tôi không thường sử dụng cấu trúc. Một điều bạn có thể xem xét là làm cho loại của bạn yêu cầu kích thước. Hãy xem xét điều này:

public enum length_dim { meters, inches } 
public enum area_dim { square_meters, square_inches } 
public class Area { 
    public Area(double a,area_dim dim) { this.area=a; this.dim=dim; } 
    public Area(Area a) { this.area = a.area; this.dim = a.dim; } 
    public Area(Length l1, Length l2) 
    { 
     Debug.Assert(l1.Dim == l2.Dim); 
     this.area = l1.Distance * l1.Distance; 
     switch(l1.Dim) { 
      case length_dim.meters: this.dim = square_meters;break; 
      case length_dim.inches: this.dim = square_inches; break; 
     } 
    } 
    private double area; 
    public double Area { get { return this.area; } } 
    private area_dim dim; 
    public area_dim Dim { get { return this.dim; } } 
} 
public class Length { 
    public Length(double dist,length_dim dim) 
    { this.distance = dist; this.dim = dim; } 
    private length_dim dim; 
    public length_dim Dim { get { return this.dim; } } 
    private double distance; 
    public double Distance { get { return this.distance; } } 
} 

Lưu ý rằng không có gì có thể được tạo từ đôi. Kích thước phải được chỉ định. Đối tượng của tôi là không thay đổi. Yêu cầu kích thước và xác minh nó sẽ ngăn chặn thảm họa Mars Express.

+1

** 1) ** Việc theo dõi các đơn vị chắc chắn sẽ là một lý do để tạo một loại tùy chỉnh. Một cách tổng quát khác để làm điều này sẽ là: 'lớp Scalar trong đó TUnit: IUnit {public Scalar (giá trị kép) {...}}', cộng với một loại đơn vị, ví dụ: 'class Meters: IUnit {…}'; sau đó sử dụng nó như 'mới Scalar (2.5) '. ** 2) ** Lý do tôi sử dụng 'struct' thay vì' class' là nó có vẻ phù hợp hơn. Có một bất lợi: người ta luôn nhận được một c'tor không tham số. Ngoài ra còn có lợi thế; ví dụ. Toán tử 'Is' của VB.NET (' object.ReferenceEquals', mà không có ý nghĩa thực sự cho các giá trị) sẽ không hoạt động. – stakx

0

Tôi không thể nói, từ trải nghiệm của mình, nếu đây là một ý hay hay không. Nó chắc chắn có ưu và khuyết điểm của nó. Một chuyên gia là bạn nhận được một liều lượng thêm về an toàn loại. Tại sao chấp nhận một đôi cho một góc khi bạn có thể chấp nhận một loại có ngữ nghĩa góc đúng (độ đến/từ radian, hạn chế đến giá trị độ 0-360, vv). Con, không phổ biến để có thể gây nhầm lẫn cho một số nhà phát triển.

Xem liên kết này cho một ví dụ thực tế của một sản phẩm thương mại thực sự mà có một số dạng như bạn mô tả:

http://geoframework.codeplex.com/

loại bao gồm Angle, Latitude, Longitude, Tốc độ, Khỏang cách.

2

Trong phần aprox. 5 năm kể từ khi tôi hỏi câu hỏi này, tôi thường chơi đùa với việc xác định các loại giá trị struct, nhưng hiếm khi thực hiện nó. Dưới đây là một số lý do tại sao:

  • Nó không đến nỗi lợi ích của họ là quá nhỏ, nhưng điều đó chi phí của việc xác định một loại giá trị struct là quá cao.Có rất nhiều mã boilerplate tham gia:

    • Override Equals và thực hiện IEquatable<T>, và ghi đè GetHashCode quá;
    • Thực hiện các toán tử ==!=;
    • Có thể triển khai IComparable<T> và các toán tử <, <=, >>= nếu loại hỗ trợ đặt hàng;
    • Ghi đè ToString và triển khai các phương thức chuyển đổi và toán tử nhập loại feom/cho các loại liên quan khác.

    Đây chỉ đơn giản là rất nhiều công việc lặp đi lặp lại thường không cần thiết khi sử dụng các loại nguyên thủy cơ bản trực tiếp.

  • Thông thường, không có giá trị mặc định hợp lý (ví dụ: đối với các loại giá trị như mã zip, ngày, giờ, v.v ...). Vì người ta không thể cấm các nhà xây dựng mặc định, xác định các loại như struct là một ý tưởng tồi và xác định chúng là class có nghĩa là một số chi phí thời gian chạy (nhiều công việc cho GC, dereferencing bộ nhớ nhiều hơn, vv)

  • I haven ' t thực sự tìm thấy rằng gói một loại nguyên thủy trong một ngữ nghĩa struct thực sự cung cấp lợi ích đáng kể đối với an toàn loại; IntelliSense/mã hoàn thành và lựa chọn tốt của tên biến/tham số có thể đạt được nhiều lợi ích giống như các loại giá trị chuyên ngành. Mặt khác, như một câu trả lời khác cho thấy, các loại giá trị tùy chỉnh có thể không trực quan đối với một số nhà phát triển, do đó, có thêm chi phí nhận thức bổ sung khi sử dụng chúng.

Đã có một số trường hợp mà tôi đã kết thúc gói một loại nguyên thủy trong một struct, bình thường khi có những hoạt động nhất định được định nghĩa trên chúng (ví dụ một DecimalDegreesRadians loại với các phương pháp để chuyển đổi giữa các).

Xác định phương trình bình đẳng và so sánh, mặt khác, không nhất thiết có nghĩa là tôi muốn xác định loại giá trị tùy chỉnh. Thay vào đó, tôi có thể sử dụng các kiểu .NET nguyên thủy và cung cấp các triển khai có tên là IEqualityComparer<T>IComparer<T>.

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