2009-12-15 44 views
14

Tôi đã suy nghĩ về cách viết các chức năng chung cho các hoạt động cơ bản như Toán Min, Max, vv Nhưng ii không biết làm thế nào để so sánh hai loại chung:C#: hàm toán học tổng quát (Min, Max, vv)

public T Max<T>(T v1, T v2) where T: struct 
{ 
    return (v1 > v2 ? v1 : v2); 
} 

Làm thế nào về điều đó?

Cảm ơn bạn.

+2

ps. Tại sao giới hạn Max cho struct? Nó có thể hữu ích như nhau đối với các lớp, ví dụ như. dây. – Will

+1

Nói chung cho bất cứ điều gì mà thực hiện 'IComparable'. – Joey

+0

Bạn nói đúng, tôi không biết về giao diện IComparable. TY. – Feryt

Trả lời

22

Nếu bạn chỉ wa nt để tạo các hàm so sánh thì bạn có thể sử dụng default comparer cho loại T. Ví dụ:

public static T Max<T>(T x, T y) 
{ 
    return (Comparer<T>.Default.Compare(x, y) > 0) ? x : y; 
} 

Nếu T cụ IComparable<T> sau đó Comparer sẽ được sử dụng; nếu T không triển khai IComparable<T> nhưng thực hiện IComparable thì trình so sánh đó sẽ được sử dụng; nếu T không triển khai IComparable<T> hoặc IComparable thì ngoại lệ thời gian chạy sẽ bị ném.

Nếu bạn muốn/cần làm nhiều hơn là chỉ so sánh các mục thì bạn có thể xem generic operators implementation in MiscUtilrelated article.

+0

Đây là giải pháp rất thanh lịch và chung chung. Tôi thích nó quá. – Feryt

+3

Đây là câu trả lời đúng, IMO. Như một sang một bên, một mô hình thay thế phổ biến là có hai tình trạng quá tải; một cái đã vượt qua 'Comparer .Default' đến giây, nó chấp nhận' IComparer '(đối với các trường hợp khi bạn muốn tùy chỉnh nó). Nhưng đối với trường hợp đơn giản, đây là con đường để đi. –

+0

Tôi không đồng ý, hãy đăng bài của tôi dưới đây –

22

Bạn có thể muốn constrain các loại tổng quát để thực hiện IComparable:

public T Max<T>(T v1, T v2) where T: struct, IComparable<T> 

và sau đó sử dụng CompareTo phương pháp:

{ 
    return (v1.CompareTo(v2) > 0 ? v1 : v2); 
} 
+0

Tuyệt vời, cảm ơn bạn. – Feryt

+1

@Feryt: Lưu ý rằng không cần phải hạn chế nó thành 'struct's. – Dario

+1

Vâng, 'struct' tránh vấn đề' null', nhưng IMO đây là câu trả lời sai; Luke có quyền của nó. –

0

Từ bộ nhớ, T cũng cần phải được IComparable (thêm rằng để các where), và sau đó bạn sử dụng v1.CompareTo(v2) > 0 vv

3

Đây là một chút quá muộn, nhưng tại sao không sử dụng các loại động và đại biểu thay thế cho IComparable? Bằng cách này bạn nhận được sự an toàn kiểu biên dịch trong hầu hết các trường hợp và sẽ chỉ gặp lỗi thời gian chạy khi các kiểu được cung cấp không hỗ trợ toán tử < và bạn không cung cấp trình so sánh mặc định làm đối số.

public static T Max<T>(T first, T second, Func<T,T,bool> f = null) 
{ 
    Func<dynamic,dynamic,bool> is_left_smaller = (x, y) => x < y ? true : false; 

    var compare = f ?? new Func<T, T, bool>((x, y) => is_left_smaller(x, y)); 

    return compare(first, second) ? second : first; 
} 
4

Hãy để tôi không đồng ý. Việc triển khai @ LukeH không phải là Chung.

tôi sẽ giải thích lý do tại sao nó không phải là Generic:

Comparer<T>.Default liên quan đến việc kiểm tra T lúc chạy-thời gian để xác định xem nó thực hiện IComparable<T>, IComparable hay không. Mặc dù hành vi này không được ghi lại đầy đủ trong http://msdn.microsoft.com/en-us/library/azhsac5f.aspx, chúng tôi có thể khấu trừ vì Comparer<T>.Default ném ngoại lệ khi T không thực hiện cả hai. Nếu việc kiểm tra được thực hiện tại thời gian biên dịch sẽ không cần một ngoại lệ (thời gian chạy), với một lỗi biên dịch thời gian là đủ.

Sau đó, như Comparer<T>.Default sử dụng Phản ánh, điều này có nghĩa là chi phí cao trên Thời gian chạy, sau đó ...., KHÔNG PHẢI là chung ... Tại sao?

Lập trình chung có nghĩa là: Một thuật toán đơn (Chung) có thể bao gồm nhiều hiệu quả cho các phiên bản viết tay.

Lấy ví dụ. Phiên bản viết tay cho số nguyên sẽ là:

public static int Max(int x, int y) 
{ 
    return (x.CompareTo(y) > 0) ? x : y; 
} 

Rất đơn giản, chỉ liên quan đến so sánh (hoặc có thể nhiều hơn, tùy thuộc vào cách Int32.CompareTo() được triển khai). Nếu chúng ta sử dụng cách thực hiện của @ LukeH, chúng ta sẽ thêm Sự phản chiếu vào một cái gì đó rất đơn giản.

Nói tóm lại:

  1. Luôn thích lỗi biên dịch thời gian để chạy thời gian Exceptions (đây không phải là Javascript, Ruby, ... :-))
  2. thực hiện này là kém hiệu quả so với phiên bản viết tay (đối với mọi loại)

Mặt khác. Bạn nghĩ Max nên trả lại khi x và y là tương đương?

tôi bắt đầu phân tích việc triển khai thực Generic ....

Việc thực hiện lý tưởng sẽ là một cái gì đó giống như ...

public static T Max<T>(T x, T y, Func<T, T, int> cmp) 
    { 
     return (cmp(x, y) > 0) ? x : y; 
    } 

    //Pseudo-code (note the 'or' next to 'where') 
    public static T Max<T>(T x, T y) where T: IComparable<T> or IComparable 
    { 
     return Max(x, y, (a, b) => { return a.CompareTo(b); }); 
    } 

này là không thể trong C#, lần thử tiếp theo có thể là ...

//pseudo-code 
    public static T Max<T>(T x, T y, Func<T, T, int> cmp) 
    { 
     return (cmp(x, y) > 0) ? x : y; 
    } 

    public static T Max<T>(T x, T y) where T: IComparable<T> 
    { 
     return Max(x, y, (a, b) => { return a.CompareTo(b); }); 
    } 

    public static T Max<T>(T x, T y) where T: IComparable 
    { 
     return Max(x, y, (a, b) => { return a.CompareTo(b); }); 
    } 

Nhưng, điều này là không thể, vì độ phân giải quá tải không tính đến Generational Constraints ....

Sau đó, tôi sẽ bỏ qua IComparable một cách có ý thức. Tôi sẽ lo lắng về việc IComparable<T>

public static T Max<T>(T x, T y, Func<T, T, int> cmp) 
    { 
     return (cmp(x, y) > 0) ? x : y; 
    } 

    public static T Max<T>(T x, T y) where T: IComparable<T> 
    { 
     return Max(x, y, (a, b) => { return a.CompareTo(b); }); 
    } 
+0

Đó là một điểm tốt về sự thiếu an toàn kiểu biên dịch. Nhưng nếu bạn định đưa ra yêu cầu về chi phí thời gian chạy, tôi thực sự muốn thấy bạn quay lại với một số con số. Tôi đã không thử nghiệm nó, nhưng tôi nghĩ rằng đi qua một chức năng sẽ gây ra một số hình phạt hiệu suất quá. –

+0

@Carl Walsh. Điểm của tôi không phải là về an toàn loại, mà là về Lập trình chung. So sánh .Default sử dụng Reflection. Được biết, Reflection có chi phí cao trong thời gian chạy, tôi nghĩ không cần thiết phải chứng minh điều đó ở đây. Đặc biệt nếu chúng ta so sánh Phản chiếu với một cái gì đó có thể được giải quyết tại thời gian biên dịch, có một sự khác biệt rất lớn về hiệu suất. Dù sao, trong một vài ngày, tôi sẽ cập nhật một số số. –

+0

@Carl Walsh. Về việc truyền hàm như một tham số, trước tiên, hàm nhận CMP như một tham số là cần thiết để so sánh một cặp phần tử với một mối quan hệ * tương đương *. Hàm thứ hai so sánh hai phần tử sử dụng * bình đẳng *. Thứ hai, liên quan đến hình phạt chuyển một hàm như một tham số, bất kỳ trình biên dịch hợp lý nào cũng có thể làm nội tuyến của CMP, sao cho mã cuối cùng sẽ giống như khi chúng ta viết A.CompareTo (b) bằng tay. –

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