2012-01-11 35 views
38
public void DoFoo<T>(T foo) where T : ISomeInterface<T> 
{ 
    //possible compare of value type with 'null'. 
    if (foo == null) throw new ArgumentNullException("foo"); 
} 

Tôi cố ý chỉ kiểm tra đối với null vì tôi không muốn hạn chế số ValueType từ bằng default(T). Mã của tôi biên dịch và hoạt động tốt theo cách này (ReSharper than phiền, nhưng không phải CodeAnalysis). Mặc dù tôi tự hỏi:So sánh một generic với null có thể là một giá trị hoặc kiểu tham chiếu?

  • Có thêm tiêu chuẩn cách xử lý tình huống này không?
  • Có khả năng xảy ra sự cố khi truy cập từ vấn đề này không?
  • Điều gì thực sự xảy ra dưới mui xe khi tôi thực hiện cuộc gọi và chuyển vào loại giá trị?

Trả lời

49

tôi đang cố chỉ kiểm tra đối với null bởi vì tôi không muốn hạn chế một ValueType từ là bằng default(T)

Đó là một cái nhìn sâu sắc, nhưng đừng lo, bạn đã được bảo hiểm ở đó. Việc so sánh T với số default(T) bằng cách sử dụng số == ở nơi đầu tiên là không hợp lệ; độ phân giải quá tải sẽ không tìm thấy toán tử == tốt nhất.

Tất nhiên, bạn có thể so sánh với .Equals nhưng sau đó bạn có nguy cơ gặp sự cố nếu người nhận là không đúng, đó chính xác là những gì bạn đang cố tránh.

Có cách nào tiêu chuẩn hơn để xử lý tình huống này không?

No. So sánh với giá trị rỗng là điều phải làm ở đây.

Khi C# đặc điểm kỹ thuật nói trong phần 7.10.6: "Các x == null xây dựng được phép mặc dù T có thể đại diện cho một loại giá trị, và kết quả chỉ đơn giản được xác định là sai khi T là một loại giá trị."

Có khả năng xảy ra sự cố khi truy cập từ vấn đề này không?

Chắc chắn. Chỉ vì biên dịch mã không có nghĩa là nó có ngữ nghĩa bạn dự định. Viết một số xét nghiệm.

Điều gì thực sự xảy ra khi tôi thực hiện cuộc gọi và chuyển vào loại giá trị?

Câu hỏi không rõ ràng.Hãy để tôi thuật lại thành hai câu hỏi:

Điều gì thực sự xảy ra khi tôi thực hiện cuộc gọi theo phương pháp chung với đối số loại là loại giá trị không thể vô hiệu?

jitter biên dịch phương thức trên lời gọi đầu tiên với cấu trúc đó. Khi jitter phát hiện việc kiểm tra null, nó thay thế nó bằng "false" vì nó biết rằng không có kiểu giá trị không nullable nào sẽ bằng null.

Điều gì thực sự xảy ra dưới mui xe khi tôi thực hiện cuộc gọi trên phương thức chung với đối số kiểu là kiểu tham chiếu nhưng đối số là loại cấu trúc? Ví dụ:

interface IFoo : ISomeInterface<IFoo> {} 
struct SFoo : IFoo { whatever } 
... 
DoFooInternal<IFoo>(new SFoo()); 

Trong trường hợp đó, jitter không thể bõ mẫu âm chót việc kiểm tra null và địa điểm cuộc gọi không thể tránh boxing. Phiên bản SFoo sẽ được đóng hộp và tham chiếu đến SFoo được đóng hộp sẽ được kiểm tra để xem nó có rỗng không.

+0

Nó vẫn làm tôi bối rối khi 'object.Equals (foo, default (T)))' trong đó 'foo' là kiểu giá trị đóng hộp trả về' false' (ví dụ 'foo' là' DateTime' nhưng thu được thông qua 'PropertyInfo '' '' GetValue'). – mayu

+0

Liệu việc kiểm tra null có được tối ưu hóa cho các loại giá trị không có giá trị hay không xảy ra trong trường hợp đó? Lý tưởng nhất là nó sẽ chuyển đổi sang cuộc gọi HasValue, nhưng từ những gì tôi có thể nói theo kinh nghiệm một kiểm tra == null chạy khá chậm đối với các kiểu giá trị nullable. – ChaseMedallion

+1

@ChaseMedallion: Câu hỏi hay - tôi nên có trong câu trả lời gốc. Tôi tin rằng jitter tạo ra các mã mới cho các loại giá trị nullable, và do đó có thể biến một kiểm tra null vào một cuộc gọi đến HasValue chứ không phải là một hộp theo sau là một kiểm tra null. Cho dù nó thực sự làm như vậy, tôi không nhớ. Nó nên được khá dễ dàng để kiểm tra mặc dù; chỉ cần viết một chương trình thử nghiệm nhỏ và xem mã x86 được tạo trong trình gỡ rối. –

10

Không, sẽ không có bất kỳ vấn đề, nhưng nếu bạn muốn cảnh báo biến mất, bạn có thể sử dụng như sau:

public void DoFoo<T>(T foo) where T : ISomeInterface<T> 
{ 
    if (ReferenceEquals(foo, null)) throw new ArgumentNullException("foo"); 
} 

Hoặc bạn có thể làm điều gì đó như thế này:

// when calling this with an actual T parameter, you have to either specify the type 
// explicitly or cast the parameter to T?. 
public void DoFoo<T>(T? foo) where T : struct, ISomeInterface<T> 
{ 
    if (foo == null) 
    { 
     // throw... 
    } 

    DoFooInternal(foo.Value); 
} 

public void DoFoo<T>(T foo) where T : class, ISomeInterface<T> 
{ 
    if (foo == null) 
    { 
     // throw... 
    } 

    DoFooInternal(foo); 
} 

private void DoFooInternal<T>(T foo) where T : ISomeInterface<T> 
{ 
    // actual implementation 
} 
Các vấn đề liên quan