2012-01-26 24 views
7

Tôi có một chục phương pháp trong dự án của tôi (C# 2.0) trông như thế này:Generics mà không mới()

internal bool ValidateHotelStayEntity(DataRow row) 
{ 
    return (new HotelStayEntity(row)).Validate(); 
} 

... nhưng đối với 'thực thể' lớp khác nhau. (Được rồi, không phải là khá tầm thường, tôi đã đơn giản hóa mã ở đây.)

Nó trông giống như một ứng cử viên tốt cho generics và tôi đến với điều này:

internal bool ValidateEntity<T>(DataRow row) where T : EntityBase 
{ 
    return (new T(row)).Validate(); 
} 

Và tất nhiên tôi nhận được " Không thể tạo một thể hiện của kiểu parametrer 'T' vì nó không có lỗi ràng buộc mới().

Vấn đề là các lớp 'thực thể' này không có một hàm tạo tham số công khai, cũng không phải là cách thêm dữ liệu 'hàng' vào sau đó. Và như EntityBase là một phần của khuôn khổ công ty tôi không kiểm soát được nó (tức là tôi không thể thay đổi nó).

Có cách nào xung quanh vấn đề này không?

+3

Tôi không có thời gian cho một ví dụ bây giờ, nhưng bạn có thể tạo một phương pháp nhà máy chung có thể được chuyển qua hàng. –

+1

Tại sao bạn đi qua DataRows thay vì các thực thể của bạn? Các thực thể có nên được xác nhận hợp lệ trước khi chúng được coi là DataRows không? –

+0

Chúng không thực sự là DataRows, chúng giống như những lỗi lầm của DataRow mà tôi đã đơn giản hóa cho bài đăng này. Tôi không muốn bị bỏ rơi với việc giải thích khuôn khổ bị áp đặt mà tôi phải giải quyết. –

Trả lời

3

Tuy nhiên, một cách khác có thể liên quan đến phản ánh với mức giá của thời gian biên dịch kiểm tra và giảm hiệu suất:

internal bool ValidateEntity<T>(DataRow row) 
{ 
    object entity = Activator.CreateInstance(typeof(T), new object[] { row }); 
    MethodInfo validate = typeof(T).GetMethod("Validate"); 
    return (bool) validate.Invoke(entity, new object[]); 
} 

Lưu ý rằng điều này sẽ làm việc ngay cả khi các đối tượng không có một tổ tiên chung

+0

Nếu Validate() sẽ ném ngoại lệ nó sẽ được gói vào TargetInvocationException, tôi không nghĩ rằng điều này tốt. Việc truyền tới EntityBase có vẻ thích hợp hơn nhiều. –

+1

Bạn có thể lưu cache MethodInfo đó trong một biến tĩnh (vì nó chung chung nó sẽ là theo kiểu đóng). Bây giờ bạn loại bỏ sự cần thiết phải phản ánh nó cho mọi cuộc gọi. –

+0

@MikeBrown Có bạn có thể nhưng bạn sẽ phải duy trì từ điển Type to MethodInfo (các lớp có thể không có tổ tiên chung) –

15

Một cách đơn giản là cung cấp một chức năng nhà máy:

internal bool ValidateEntity<T>(DataRow row, Func<DataRow, T> factory) 
    where T : EntityBase 
{ 
    return factory(row).Validate(); 
} 

và gọi với:

bool valid = ValidateEntity(row, x => new Foo(x)); 

Tâm trí bạn, vào thời điểm mà nó phức tạp hơn chỉ gọi

bool valid = new Foo(row).Validate() 

ở nơi đầu tiên ...

Nó không thực sự rõ ràng những gì bạn đang cố gắng đạt được trong bối cảnh thực tế của bạn, nhưng cách tiếp cận nhà máy/nhà cung cấp này chắc chắn có thể hữu ích vào những lúc khác. Lưu ý rằng việc gọi đại diện nhà máy cũng có thể là đáng kể nhanh hơn sử dụng new T() với một ràng buộc, as I blogged a while ago. Irrevelant trong nhiều trường hợp, nhưng đáng biết.

EDIT: Để tương thích .NET 2.0 bạn cần phải khai báo các đại biểu cho mình, nhưng đó là dễ dàng:

public delegate TResult Func<T, TResult>(T input); 

Nếu bạn thực sự sử dụng C# 2 (chứ không phải là, nói, C# 3 nhắm mục tiêu NET 2.0) sau đó bạn sẽ không thể sử dụng biểu thức lambda hoặc, nhưng bạn vẫn có thể sử dụng phương pháp vô danh:

bool valid = ValidateEntity(row, delegate(DataRow x) { return new Foo(x); }); 
+1

Tôi không nghĩ rằng có một Func trong 2.0. –

+0

@PeterTrevor: Rất tiếc, đã không nhận thấy yêu cầu .NET 2.0 - nhưng bạn có thể dễ dàng xác định các đại biểu của riêng bạn. Sẽ chỉnh sửa để đưa ra một ví dụ về điều đó. –

+0

+1 Vì nhà máy sẽ tốt hơn so với phản xạ. – Shibumi

1

Một ví dụ về giải pháp phương pháp nhà máy @JohnSaunders':

internal bool ValidateEntity<T>(DataRow row, Func<DataRow, T> factory) where T : EntityBase 
{ 
    return (factory(row)).Validate(); 
} 
1

Bạn có thể làm một cái gì đó như thế này:

internal bool ValidateEntity<T>(DataRow row) where T : EntityBase 
{ 
    return ((T)Activator.CreateInstance(typeof(T), new object[] { row })).Validate(); 
}