2012-02-18 24 views
5

thể trùng lặp:
C# generic constraint for only integersLàm thế nào để tổng hợp các số chung trong C#?

Như bạn có thể thấy trong đoạn mã sau, tôi cần phải tính toán tổng của hai số chung.

public class NumberContainer<T> 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public T Total { get { return ValueA + ValueB; } } 
} 

Tuy nhiên, nó không phải là có thể làm một bổ sung trực tiếp của hai giá trị T, mà kết quả trong các lỗi biên dịch dưới đây: không thể được áp dụng cho toán hạng kiểu

Operator '+' 'T' và 'T'

cho rằng tôi không có ý định sử dụng T cho bất cứ điều gì khác hơn là giá trị các loại đại diện cho số (ngắn, ushort, int, uint, vv), làm thế nào tôi có thể thực hiện tổng? (hiệu quả là yếu tố được xem xét)

+0

Hoặc có thể: [Có C# chung hạn chế cho “số thực” loại?] (http://stackoverflow.com/questions/1348594/is-there-ac-sharp-generic-constraint-for-real-number-types/1348625#1348625) –

+1

Câu hỏi được đóng dưới dạng bản sao câu hỏi tìm cách đặt một ràng buộc vào 'T', đó là * không * câu hỏi này đang cố gắng làm gì. Tôi nghĩ rằng nó nên được mở cửa trở lại. – dasblinkenlight

Trả lời

12

Bạn có thể làm điều đó với "chút ma thuật" từ LINQ:

private static readonly Func<T, T, T> adder; 
static NumberContainer() { 
    var p1 = Expression.Parameter(typeof (T)); 
    var p2 = Expression.Parameter(typeof (T)); 
    adder = (Func<T, T, T>)Expression 
     .Lambda(Expression.Add(p1, p2), p1, p2) 
     .Compile(); 
} 
public T Total { get { return adder(ValueA, ValueB); } } 

Hạn chế duy nhất là mã này sẽ biên dịch ngay cả khi NumberContainer được khởi tạo với một loại T mà không hỗ trợ bổ sung; tất nhiên nó sẽ ném một ngoại lệ vào thời gian chạy. Một lợi ích bổ sung là này cần hoạt động với các toán tử do người dùng định nghĩa + do người dùng xác định.

1

Bạn có thể chỉ định ràng buộc cho T rằng nó phải là cấu trúc và có thể nó thực thi IConvertable, sau đó sử dụng Convert.

public class NumberContainer<T> 
     where T : struct, IConvertible 
    { 
     public T ValueA { get; private set; } 
     public T ValueB { get; private set; } 
     public T Total { 
      get 
      { 
       // do type checking here, then: 

       return (T)Convert.ChangeType(
        Convert.ToDouble((object)ValueA) + 
        Convert.ToDouble((object)ValueB), typeof(T)); 
      } 
     } 
    } 

Tuy nhiên, không có cách nào để Generics đảm bảo rằng T là một số nguyên hoặc kiểu thả nổi lúc biên dịch. Bạn có thể kiểm tra nó trong thời gian chạy, mặc dù, và ném một ngoại lệ.

+0

Điều này cũng không hoạt động. Trong thực tế, đề xuất của bạn thậm chí không biên dịch. –

+0

Nó đã tắt khỏi đầu của tôi, cố định và biên dịch. Nếu bạn muốn giải thích thêm về lý do tại sao nó không hoạt động, điều đó sẽ hữu ích hơn rất nhiều. – HackedByChinese

+0

DateTime là một cấu trúc và một IConvertible nhưng không thể được chuyển đổi thành gấp đôi vì vậy sẽ chỉ ném một ngoại lệ bất cứ lúc nào bạn sẽ gọi nó với các tham số DateTime. –

5

Ngoài việc sử dụng LINQ, nếu bạn đang ở trên NET 4 này có thể sử dụng dynamic:

static T Add<T>(T x, T y) 
{ 
    dynamic dx = x, dy = y; 
    return dx + dy; 
} 

Trong mẫu mã của bạn này biến thành:

public class NumberContainer<T> where T: struct 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public T Total { get { return ((dynamic)ValueA) + ((dynamic)ValueB); } } 
} 

Cách tiếp cận này doesn' t đảm bảo an toàn mặc dù. Nếu T là một số cấu trúc ngẫu nhiên không hỗ trợ +, bạn sẽ kết thúc với một ngoại lệ (tôi nghĩ).

+0

Sử dụng đáng sợ năng động, đặc biệt là các nhà khai thác xem xét không phải là ảo ... Tôi đoán nó sẽ "chỉ hoạt động" bởi hộp ma thuật của nó. (Tôi sẽ có các loại cấu trúc được ưu tiên * cũng *: - /) –

+0

@pst - Đồng ý, nhưng nó có thể hoạt động. Tôi hiểu điểm này. –

1

Bạn có thể điều trị này cũng giống như một lớp cơ sở trừu tượng và lấy được các lớp học cụ thể mà xác định giá trị T và cung cấp việc thực hiện cho Tổng

public abstract class NumberContainer<T> 
    where T: struct 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public abstract T Total(); 
} 

public class IntContainer : NumberContainer<int> 
{ 
    public override int Total() 
    { 
     return ValueA + ValueB; 
    } 
} 
+1

Nếu một 'int' có thể được chuyển đổi thành' T' thì điều này sẽ không thành vấn đề. Mã của bạn không biên dịch. –

+0

ngớ ngẩn tôi. Đã cập nhật câu trả lời để cung cấp giải pháp thay thế vì hiện tại có một số giải pháp sử dụng động lực và liên kết –

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