2009-12-09 31 views
27

tôi có phương pháp mở rộng sau:Có thể tái cấu trúc phương pháp mở rộng này không?

public static void ThrowIfArgumentIsNull<T>(this T value, string argument) 
    where T : class 
{ 
    if (value == null) 
    { 
     throw new ArgumentNullException(argument); 
    } 
} 

và đây là một ví dụ về việc sử dụng của nó ....

// Note: I've poorly named the argument, on purpose, for this question. 
public void Save(Category qwerty) 
{ 
    qwerty.ThrowIfArgumentIsNull("qwerty"); 
    .... 
} 

hoạt động 100% sử dụng tốt.

Nhưng, tôi không thích cách tôi phải cung cấp tên biến, chỉ để giúp thông báo ngoại lệ của tôi.

Tôi đã tự hỏi nếu nó có thể cấu trúc lại các phương pháp khuyến nông, vì vậy nó có thể được gọi như thế này ...

qwerty.ThrowIfArgumentIsNull(); 

và nó sẽ tự động hiểu ra rằng tên của biến là 'qwerty' và do đó sử dụng nó làm giá trị cho ArgumentNullException.

Có thể? Tôi giả định phản ánh có thể làm điều này?

+0

cũng xem http://stackoverflow.com/questions/869610/c-resolving-a-parameter-name-at-runtime/869629#869629 –

+4

Kiểm tra http://msmvps.com/blogs/jon_skeet/archive/ 2009/12/09/quot-magic-quot-null-argument-testing.aspx - blogged :) –

+0

Tôi nghĩ rằng đây là một giải pháp rất khó khăn cho một vấn đề rất đơn giản ... Nếu bạn đang sử dụng Visual Studio, bạn có thể sử dụng đoạn mã để thực hiện điều này rất dễ dàng;) –

Trả lời

34

Không, bạn không thể thực hiện việc này. Nó sẽ được tốt đẹp, nhưng nó không thể mà không có một số loại AOP tham gia. Tôi chắc chắn PostSharp có thể làm một công việc tốt đẹp, hy vọng sử dụng các thuộc tính, và trong Bộ luật Hợp đồng nó sẽ chỉ được:

Contract.Requires(qwerty != null); 

Lý tưởng tôi muốn một thuộc tính PostSharp mà tạo ra các hợp đồng Mã gọi - và tôi sẽ chơi xung quanh với điều đó tại một số điểm - nhưng cho đến lúc đó, phương pháp mở rộng mà bạn đã có là cách tiếp cận tốt nhất mà tôi đã tìm thấy ...

(Nếu tôi đã thử cách tiếp cận Hợp đồng PostSharp + Mã, tôi chắc chắn sẽ blog về nó, btw ... Mono Cecil có thể làm cho nó hợp lý dễ dàng quá.)

EDIT: Để mở rộng câu trả lời của Laurent, bạn có khả năng có :

new { qwerty }.CheckNotNull(); 

Và nếu bạn có rất nhiều tham số không nullable, bạn có thể có:

new { qwerty, uiop, asdfg }.CheckNotNull(); 

này sẽ phải dùng phản ánh để làm việc ra các tài sản. Có nhiều cách mà bạn có thể tránh làm việc phản chiếu trên mọi truy cập, xây dựng một đại biểu cho mỗi tài sản và thường làm cho nó trở nên rùng mình. Tôi có thể điều tra điều này cho một bài đăng blog ... nhưng nó hơi icky, và tôi thích ý tưởng có thể chỉ thuộc tính các tham số ...

EDIT: Mã được triển khai và blog post được thực hiện hợp lệ. Ick, nhưng vui vẻ

+0

Cảm ơn Jon vì câu trả lời nhanh :) –

+1

và +1 cho bài đăng trên blog :) tôi cũng có thể xem câu trả lời của Laurnet ... –

+4

Tôi đã đọc bài đăng trên blog của bạn và bạn thành thật không phải trải qua tất cả hullabaloo đó. Chỉ cần thực hiện một phương thức 'ThrowIfNull()' mà không có tham số và để cho stack-trace toting developer đi lên stack một chút để tìm ra đối số nào là null. Chỉ cần một ý nghĩ :) – RCIX

3

Trong một từ: no.

Phương thức mở rộng được chuyển một giá trị. Nó không có ý tưởng nơi mà giá trị đến từ hoặc những gì định danh người gọi có thể đã chọn để tham khảo nó như là.

1

tôi sẽ khuyên bạn nên thay vì làm như sau:

public static void ThrowIfArgumentIsNull(this object value, string argument) 
{ 
    if (value == null) 
    { 
     throw new ArgumentNullException(argument); 
    } 
} 

Sử dụng Generics trong trường hợp này dường như không thêm bất kỳ giá trị. Nhưng như câu hỏi ban đầu của bạn, tôi không nghĩ rằng đó là có thể.

+2

Sử dụng Generics cho phép phương pháp loại trừ các loại giá trị. Lưu ý 'where T: class' – Greg

1

Xem thêm ArgumentNullException and refactoring để biết các giải pháp hoàn chỉnh dọc theo các dòng giống như câu trả lời .

gì về:

public void Save(Category qwerty) 
{ 
    ThrowIfArgumentIsNull(() => return qwerty); 
    qwerty.ThrowIfArgumentIsNull("qwerty");  
    // .... 
} 

sau đó xác định ThrowIfArgumentIsNull như

public static void ThrowIfArgumentIsNull(Expression<Func<object>> test) 
{ 
    if (test.Compile()() == null) 
    { 
     // take the expression apart to find the name of the argument 
    } 
} 

xin lỗi tôi không có thời gian để điền vào các chi tiết hoặc cung cấp mã đầy đủ hiện nay.

+0

Bạn không cần phần' return' trong lambda. –

2

Tôi thấy dễ nhất để thực hiện việc này bằng đoạn mã.

Trong ví dụ của bạn, tôi có thể nhập tna<tab>qwerty<enter>.

Dưới đây là đoạn:

<?xml version="1.0" encoding="utf-8" ?> 
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> 
    <CodeSnippet Format="1.0.0"> 
     <Header> 
       <Title>Check for null arguments</Title> 
       <Shortcut>tna</Shortcut> 
       <Description>Code snippet for throw new ArgumentNullException</Description> 
       <Author>SLaks</Author> 
       <SnippetTypes> 
         <SnippetType>Expansion</SnippetType> 
         <SnippetType>SurroundsWith</SnippetType> 
       </SnippetTypes> 
     </Header> 
     <Snippet> 
       <Declarations> 
         <Literal> 
           <ID>Parameter</ID> 
           <ToolTip>Paremeter to check for null</ToolTip> 
           <Default>value</Default> 
         </Literal> 
       </Declarations> 
       <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$"); 
     $end$]]> 
       </Code> 
     </Snippet> 
    </CodeSnippet> 
</CodeSnippets> 
1

Tôi thích Enforce từ Lokad Shared Libraries.

cú pháp cơ bản:

Enforce.Arguments(() => controller,() => viewManager,() => workspace); 

này sẽ ném một ngoại lệ với tên tham số và kiểu nếu bất kỳ đối số là null.

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