2013-03-03 26 views
6

Là một dự án sở thích (và đắm mình sâu hơn trong các phương pháp generics/extension), tôi đang viết một thư viện kiểm tra tham số!Làm thế nào một phương thức mở rộng có thể được gắn vào một lớp chung khi đối số kiểu là IEnumerable <T>?

Tôi có một mô hình gọi là Đối số mô tả một tham số và trông như thế này:

public class Argument<T> 
{ 
    internal Argument(string name, T value) 
    { 
     Name = name; 
     Value = value; 
    } 

    public string Name { get; private set; } 

    public T Value { get; private set; } 
} 

Khi xác nhận cho một tham số bắt đầu, một thể hiện của đối tượng này được tạo ra, và kiểm chứng thực cá nhân được thực hiện bằng cách gọi phương pháp khuyến nông (có chứa logic thực tế) treo trên đó.

Một phương pháp gia hạn xác nhận rằng một bộ sưu tập có chứa ít nhất một mục, và hiện trông như thế này:

public static Argument<IEnumerable<T>> HasItems<T>(this Argument<IEnumerable<T>> argument) 
{ 
    if (!argument.Value.Any()) 
     throw Error.Generic(argument.Name, "Collection contains no items."); 

    return argument; 
} 

Nhưng nó không xuất hiện để làm việc. Nếu tôi là, nói, để viết bài kiểm tra đơn vị này:

[TestMethod] 
public void TestMethod1() 
{ 
    var argument = new List<int>() { 1, 2, 6, 3, -1, 5, 0 }; 

    Validate.Argument("argument", argument) 
     .IsNotNull() 
     .HasItems() 
     .All(v => v.IsGreaterThan(0)); 
} 

HasItems không hiển thị trong Intellisense, tôi nhận được lỗi biên dịch này:

'Validation.Argument<System.Collections.Generic.List<int>>' does not contain a definition for 'HasItems' and no extension method 'HasItems' accepting a first argument of type 'Validation.Argument<System.Collections.Generic.List<int>>' could be found (are you missing a using directive or an assembly reference?)

Và nếu tôi cố gắng vượt qua đánh giá trực tiếp vào phương pháp khuyến nông, như vậy:

CollectionTypeExtensions.HasItems(Validate.Argument("argument", argument)); 

tôi có được điều này:

The best overloaded method match for 'Validation.CollectionTypeExtensions.HasItems<int>(Validation.Argument<System.Collections.Generic.IEnumerable<int>>)' has some invalid arguments

Dựa trên nghiên cứu của tôi, những gì tôi cần cho việc này đến công việc được gọi là "sai", và áp dụng cho các giao diện và các đại biểu, nhưng không phải đến các lớp học (ví dụ:. Mọi tầng lớp là bất biến)

Đó nói, nó có thể hoạt động theo cách khác. Một mà nói đến cái tâm là để viết lại nó để đi thẳng đến T, như vậy:

public static Argument<T> HasItems<T, TElement>(this Argument<T> argument) 
     where T : IEnumerable<TElement> 
{ 
    if (!argument.Value.Any()) 
     throw Error.Generic(argument.Name, "Collection contains no items."); 

    return argument; 
} 

..nhưng điều đó không làm việc, hoặc vì nó đòi hỏi TElement được xác định một cách rõ ràng khi phương pháp này được gọi. Tôi cũng có thể quay trở lại sử dụng giao diện IEnumerable không chung chung trong ràng buộc kiểu, nhưng sau đó tôi phải tìm cách để ép buộc IEnumerable thành IEnumerable (yêu cầu phải biết T là gì trong ngữ cảnh đó), hoặc nhân bản chức năng của Any() để kiểm tra sự tồn tại của bất kỳ mục nào, có một phương pháp mở rộng khác (Tất cả) mà sẽ rất, rất lộn xộn, vì vậy tôi muốn tránh nó.

Vì vậy, cuối cùng, tôi đoán câu hỏi của mình là: làm cách nào để phương pháp tiện ích mở rộng của tôi được đính kèm đúng cách?

Trả lời

2

Tính năng này có phù hợp với bạn không? Có vẻ hơi thô lỗ, nhưng nó thực sự hoạt động.

public static Argument<T> HasItems<T>(this Argument<T> argument) where T: IEnumerable 
{ 
    if (!argument.Value.Cast<object>().Any()) 
    { 
     throw Error.Generic(argument.Name, "Collection contains no items."); 
    } 

    return argument; 
} 
+0

Đó ... không thực sự làm việc! Khi tôi nhìn vào điều này với đôi mắt tươi sáng sáng nay, tôi đã đi một con đường khác, nhưng tôi đánh dấu là câu trả lời vì nó giải quyết vấn đề một cách ngắn gọn như đã trình bày. Cảm ơn! –

0

Tôi tin vào những gì bạn thực sự muốn là một giao diện IArgument đó là hiệp biến trên T:

public static class Validate 
{ 
    public static IArgument<T> Argument<T>(string name, T value) 
    { 
     return new Argument<T>(name, value); 
    } 
} 

public interface IArgument<out T> 
{ 
    string Name { get; } 
    T Value { get; } 
} 

public class Argument<T> : IArgument<T> 
{ 
    internal Argument(string name, T value) 
    { 
     Name = name; 
     Value = value; 
    } 

    public string Name { get; private set; } 

    public T Value { get; private set; } 
} 

public static class ExtensionMethods 
{ 
    public static IArgument<T> IsNotNull<T>(this IArgument<T> argument) 
    { 
     return argument; 
    } 

    public static IArgument<IEnumerable<T>> HasItems<T>(this IArgument<IEnumerable<T>> argument) 
    { 
     return argument; 
    } 

    public static IArgument<IEnumerable<T>> All<T>(this IArgument<IEnumerable<T>> argument, Predicate<T> predicate) 
    { 
     return argument; 
    } 
} 

[TestMethod] 
public void TestMethod1() 
{ 
    List<int> argument = new List<int>() { 1, 2, 6, 3, -1, 5, 0 }; 

    Validate.Argument("argument", argument) 
     .IsNotNull() 
     .HasItems() 
     .All(v => v > 0); 
} 
Các vấn đề liên quan