2009-03-26 35 views
11

Tôi cố gắng để tạo ra một lớp PredicateBuilder<T> mà kết thúc tốt đẹp một Expression<Func<T, bool>> và cung cấp một số phương pháp để dễ dàng xây dựng một biểu thức với nhiều AndOr phương pháp. Tôi nghĩ sẽ thật thú vị nếu tôi có thể sử dụng trực tiếp số PredicateBuilder<T> này làm số Expression<Func<T, bool>> và nghĩ rằng điều này có thể được thực hiện bằng cách có phương thức implicit operator.C#: điều hành ngầm và mở rộng các phương pháp

Stripped xuống phiên bản của lớp trông như thế này:

class PredicateBuilder<T> 
{ 
    public Expression<Func<T, bool>> Predicate { get; protected set; } 

    public PredicateBuilder(bool initialPredicate) 
    { 
     Predicate = initialPredicate 
      ? (Expression<Func<T, bool>>) (x => true) 
      : x => false; 
    } 

    public static implicit operator Expression<Func<T, bool>>(
     PredicateBuilder<T> expressionBuilder) 
    { 
     return expressionBuilder.Predicate; 
    } 
} 

Sau đó, cũng giống như một bài kiểm tra, tôi có phương pháp gia hạn này trong một lớp tĩnh:

public static void PrintExpression<T>(this Expression<Func<T, bool>> expression) 
{ 
    Console.WriteLine(expression); 
} 

Trong đầu tôi, tôi sau đó có thể thực hiện những việc sau:

var p = new PredicateBuilder<int>(true); 

p.PrintExpression(); 
PredicateExtensions.PrintExpression(p); 

Tuy nhiên, không ai trong số họ làm việc. Đối với người đầu tiên, phương pháp mở rộng không được tìm thấy. Và thứ hai, nó nói rằng

Các đối số kiểu cho phương thức 'ExtravagantExpressions.PredicateHelper.PrintExpression (System.Linq.Expressions.Expression>)' không thể suy ra từ cách sử dụng. Hãy thử xác định các đối số kiểu một cách rõ ràng.

Vì vậy, tôi đã cố gắng điều sau đây, mà làm việc:

PredicateExtensions.PrintExpression<int>(p); 

Ngoài ra, công trình này, tất nhiên:

((Expression<Func<int, bool>>) p).PrintExpression(); 

Nhưng yeah ... tại sao không phải là người khác làm việc? Tôi đã hiểu nhầm điều gì đó về cách hoạt động của điều này implicit operator?

+3

Cảm ơn bạn đã dọn dẹp! Tôi tiếp tục viết phần mở rộng thay vì gia hạn ... Tôi không biết tại sao! Tôi chỉ ... không thể ... dừng lại ... = S – Svish

Trả lời

11

Điều này không dành riêng cho các phương pháp mở rộng. C# sẽ không ngầm đúc một đối tượng sang kiểu khác trừ khi có một đầu mối về kiểu đích. Giả sử như sau:

class A { 
    public static implicit operator B(A obj) { ... } 
    public static implicit operator C(A obj) { ... } 
} 

class B { 
    public void Foo() { ... } 
} 

class C { 
    public void Foo() { ... } 
} 

Bạn sẽ được gọi phương pháp nào trong tuyên bố sau?

new A().Foo(); // B.Foo? C.Foo? 
+0

Tôi sẽ nói cả hai. Không, đoán đó là một điểm, hehe. – Svish

+3

Tôi mong đợi "Foo() là mơ hồ: B.Foo() hoặc C.Foo()" –

+1

@Anton: Điều đó là có thể, nhưng sẽ làm phức tạp ngôn ngữ và có thể ẩn các tác dụng phụ. Và sau tất cả, bạn cảm thấy thế nào nếu một mã làm việc đột nhiên phá vỡ khi bạn định nghĩa toán tử ngầm mới trên một lớp :) Nó đơn giản hơn để buộc khai báo kiểu rõ ràng ở khắp mọi nơi. –

2

Không, bạn chưa, nhưng khấu trừ loại C# của trình biên dịch không đủ mạnh để hiểu mã của bạn và đặc biệt, nó không nhìn vào các toán tử ngầm. Bạn sẽ phải gắn bó với Expression<Func<T,bool>> - tại sao không có phương pháp mở rộng như Or, And trực tiếp trên các biểu thức?

+0

aha, ok, vì vậy về mặt kỹ thuật nó nên hoạt động, nó chỉ là nó không tìm kiếm các phương pháp mở rộng trên các công cụ loại bỏ điều hành ngầm? – Svish

+0

Tôi muốn nói "lý tưởng" nó nên hoạt động :) Chỉ cần như vậy, nó không đủ mạnh để biết nơi để tìm. –

+0

Đã có các phương pháp mở rộng đó. Nhưng tôi đang cố gắng làm mọi việc dễ dàng hơn một chút khi xây dựng các biểu thức như thế này. Không chắc chắn nếu tôi sẽ thành công mặc dù: p – Svish

0

Như Anton nói, nếu bạn đặt phương thức tiện ích mở rộng trực tiếp trên Expression<Func<...>> thì có thể nó sẽ hoạt động.

Giải thích thêm ... không có gì đặc biệt thông minh, nhưng ý tưởng sẽ là bạn không có lớp học PredicateBuilder mà bạn tạo các phiên bản. Thay vào đó bạn chỉ có khối xây dựng hoàn toàn tĩnh:

public static class Predicates 
{ 
    public static Expression<Func<T, bool>> True<T>() 
    { 
     return x => true; 
    } 

    public static Expression<Func<T, bool>> False<T>() 
    { 
     return x => false; 
    } 

    public static Expression<Func<T, bool>> And<T>(
     this Expression<Func<T, bool>> left, 
     Expression<Func<T, bool>> right) 
    { 
     return ... // returns equivalent of (left && right) 
    } 
} 

Hai chức năng TrueFalse đóng vai trò của constructor PredicateBuilder(bool) của bạn, và bạn có lẽ muốn có những cái tương tự để so sánh nguyên thủy và như vậy, và sau đó các nhà khai thác như And sẽ cho phép bạn kết nối hai biểu thức với nhau.

Tuy nhiên, bạn mất khả năng sử dụng ký hiệu toán tử mà bạn có thể đã sử dụng với đối tượng bao bọc của mình và thay vào đó bạn phải sử dụng tên phương thức. Tôi đã chơi xung quanh với cùng một loại phương pháp tiếp cận và điều tôi luôn quay lại là tôi muốn có thể xác định các nhà khai thác mở rộng. Nhóm C# dường như đã xem xét những điều này cho 3.0 (cùng với các thuộc tính mở rộng) nhưng chúng được ưu tiên thấp hơn vì chúng không đóng một phần trong mục tiêu tổng thể của LINQ.

+0

Bạn có ý gì bằng cách đặt phương thức mở rộng trực tiếp trên 'Expression >' "? Đó không phải là những gì tôi đã làm chưa? Hoặc bạn có nghĩa là sử dụng nó trên một 'Expression >' thay vì trên một 'PredicateBuilder '? (Đó sẽ chỉ là cách sử dụng bình thường và tất nhiên là hoạt động) – Svish

+0

Xem phần giải thích. –

+0

Tôi không hiểu ... – Svish

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