2012-06-08 38 views
7

Có thể thực hiện số học cơ bản (ít nhất là bổ sung) trong C# generics, giống như bạn có thể with C++ templates? Tôi đã cố gắng một thời gian để có được chúng và làm việc, nhưng C# không cho phép bạn khai báo cùng một kiểu generic nhiều lần, giống như bạn có thể với các khuôn mẫu.Thực hiện số học trong generics?

Googling mở rộng không cung cấp câu trả lời.

EDIT: Cảm ơn, nhưng những gì tôi đang tìm kiếm là một cách để làm số học tại thời gian biên dịch, nhúng một cái gì đó giống như số Giáo hội trong các loại generics. Đó là lý do tại sao tôi liên kết bài viết mà tôi đã làm. Số học trong loại chung, không phải là số học trên các trường hợp của loại chung.

+3

Thật không may, loại hạn chế không cho phép bạn để yêu cầu các loại hỗ trợ các nhà khai thác arithmethic. Điều tôi thấy thú vị là trong mã nguồn BCL cho ví dụ: 'Int32' bạn sẽ tìm thấy một giao diện' IArithmetic 'trong danh sách thừa kế được nhận xét. Đây là suy đoán thuần túy về phía tôi nhưng nếu Microsoft kích hoạt giao diện đó trong BCL thì bạn có thể chỉ định 'IArithmetic ' như một ràng buộc để cho phép bạn viết các lớp generic của riêng bạn với số học. –

+0

Liên kết đến câu hỏi tương tự: http://stackoverflow.com/q/4039694/613130 ​​ – xanatos

Trả lời

4

Đáng tiếc là bạn không thể sử dụng phép tính số học trên các loại generic

T Add(T a, T b) 
{ 
    return a + b; // compiler error here 
} 

sẽ không hoạt động trong C#!

Nhưng bạn có thể tạo các loại số của riêng mình và quá tải toán tử (số học bình đẳng và implicit, explicit). Điều này cho phép bạn làm việc với họ một cách khá tự nhiên. Tuy nhiên bạn không thể tạo một hệ thống phân cấp thừa kế với Generics. Bạn sẽ phải sử dụng một lớp cơ sở không chung chung hoặc giao diện.

Tôi chỉ làm điều đó với loại véc tơ.Một phiên bản rút gọn ở đây:

public class Vector 
{ 
    private const double Eps = 1e-7; 

    public Vector(double x, double y) 
    { 
     _x = x; 
     _y = y; 
    } 

    private double _x; 
    public double X 
    { 
     get { return _x; } 
    } 

    private double _y; 
    public double Y 
    { 
     get { return _y; } 
    } 

    public static Vector operator +(Vector a, Vector b) 
    { 
     return new Vector(a._x + b._x, a._y + b._y); 
    } 

    public static Vector operator *(double d, Vector v) 
    { 
     return new Vector(d * v._x, d * v._y); 
    } 

    public static bool operator ==(Vector a, Vector b) 
    { 
     if (ReferenceEquals(a, null)) { 
      return ReferenceEquals(b, null); 
     } 
     if (ReferenceEquals(b, null)) { 
      return false; 
     } 
     return Math.Abs(a._x - b._x) < Eps && Math.Abs(a._y - b._y) < Eps; 
    } 

    public static bool operator !=(Vector a, Vector b) 
    { 
     return !(a == b); 
    } 

    public static implicit operator Vector(double[] point) 
    { 
     return new Vector(point[0], point[1]); 
    } 

    public static implicit operator Vector(PointF point) 
    { 
     return new Vector(point.X, point.Y); 
    } 

    public override int GetHashCode() 
    { 
     return _x.GetHashCode()^_y.GetHashCode(); 
    } 

    public override bool Equals(object obj) 
    { 
     var other = obj as Vector; 
     return other != null && Math.Abs(other._x - _x) < Eps && Math.Abs(other._y - _y) < Eps; 
    } 

    public override string ToString() 
    { 
     return String.Format("Vector({0:0.0000}, {1:0.0000})", _x, _y); 
    } 
} 
+0

Bạn có nghĩa là "hạn chế' struct' không được phép "? –

+0

Bạn thực sự có thể. Xem [Các ràng buộc về các tham số kiểu (Hướng dẫn lập trình C#)] (http://msdn.microsoft.com/en-us/library/d5x73970.aspx). –

+0

@AdamHouldsworth: Các đường dẫn có thể triển khai giao diện! Vì các cấu trúc không thể thừa hưởng lẫn nhau, nên hạn chế đối với một kiểu cấu trúc nhất định sẽ giống như không có tham số chung (hoặc bạn sẽ có một tham số kiểu generic hợp lệ, cụ thể là cấu trúc này)! –

3

Vui lòng cung cấp thêm giải thích rõ ràng nếu câu trả lời của tôi có vẻ không cân bằng.

Không có ràng buộc chung về các toán tử trong ngôn ngữ C#, ít nhất. Như Jon Skeet đã chứng minh với Unconstrained Melody, các ràng buộc thực sự có thể hoàn toàn hợp lệ trong chính CLR.

Điều tốt nhất bạn có thể làm với các ràng buộc là cung cấp các giao diện/lớp tùy chỉnh hiển thị các hành động bạn cần. Bạn sẽ không thể cung cấp nguyên thủy (trừ khi bạn cũng thực hiện toán tử implicit), nhưng ít nhất nó sẽ cho phép bạn tạo mã chung cho phần toán học.

Ràng buộc chung cho phép trình biên dịch phỏng đoán các thành viên có sẵn dựa trên mẫu số chung thấp nhất (như được chỉ định bởi ràng buộc hoặc thiếu). Hầu hết thời gian, Generics là không bị giới hạn và do đó cung cấp cho bạn chỉ object ngữ nghĩa.


Ngoài ra, tránh sử dụng những hạn chế và sử dụng dynamic để tạm thời lưu trữ các biến chung chung và sau đó đưa ra giả định (thông qua gõ vịt) mà nó có các nhà khai thác có liên quan:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var result = Add<int, long, float>(1, 2); 
     Console.WriteLine(result); // 3 
     Console.WriteLine(result.GetType().FullName); // System.Single 
     Console.Read(); 
    } 

    static T3 Add<T1, T2, T3>(T1 left, T2 right) 
    { 
     dynamic d1 = left; 
     dynamic d2 = right; 
     return (T3)(d1 + d2); 
    } 
} 

này liên quan đến DLR và sẽ có một số chi phí hoạt động (tôi không có số liệu chính xác), đặc biệt nếu bạn dự định tính toán là quan trọng về hiệu suất.


Tôi không chắc chắn những gì bạn có nghĩa là "tuyên bố cùng loại generic nhiều lần", hoạt động này:

class Tuple<T1, T2> // etc. 

var myTuple = new Tuple<int, int>(1, 2); 
0

Bạn bè, câu trả lời trực quan để điều này trong C# là RTTI và đúc lại từ lớp đối tượng

enter code here 

class MyMath 
{ 
    public static T Add<T>(T a, T b) where T: struct 
    { 
     switch (typeof(T).Name) 
     { 
      case "Int32": 
       return (T) (object)((int)(object)a + (int)(object)b); 
      case "Double": 
       return (T)(object)((double)(object)a + (double)(object)b); 
      default: 
       return default(T); 
     } 
    } 
} 

class Program 
{ 
    public static int Main() 
    { 
     Console.WriteLine(MyMath.Add<double>(3.6, 2.12)); 
     return 0; 
    } 
} 
+0

Tôi là người mới bắt đầu trong C# và bất kỳ lời giải thích đơn giản nào là quan trọng vì vậy nếu ai đó bình chọn, tôi sẽ đánh giá cao nhận xét về điều đó! Bây giờ, có thể ai đó (khác) xin vui lòng giải thích tại sao câu trả lời này đã bỏ phiếu xuống? Tại sao phương pháp này không chính xác? – Celdor

+1

@Celdor: đầu tiên, nó sẽ chậm, bởi vì bạn đang thực hiện RTTI và so sánh các chuỗi. Thứ hai, nó không thực sự chung chung nếu tôi phải thêm tất cả các loại bằng tay ... –

+1

Gần đây, jit đã giới thiệu optmization phát hiện typeof (T) và loại bỏ các nhánh chết dựa trên điều kiện RTTI. Giải pháp này, với các sửa đổi thích hợp, có thể chạy nhanh hơn các câu trả lời khác. – Luca

-1

có, nó có thể được thực hiện bằng cách sử dụng các biến kiểu động .

dụ:

T Add(T value1, T value2) 
{   
     dynamic a = value1; 
     dynamic b = value2; 
     return (a + b); 
} 

để tham khảo chi tiết xin click here

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