Làm thế nào để kiểm tra nếu một đối tượng là nullable?


202

Làm cách nào để kiểm tra xem một đối tượng đã cho là nullable hay nói cách khác là cách thực hiện phương thức sau ...

bool IsNullableValueType(object o)
{
    ...
}

EDIT: Tôi đang tìm kiếm các loại giá trị nullable. Tôi đã không có loại ref trong tâm trí.

//Note: This is just a sample. The code has been simplified 
//to fit in a post.

public class BoolContainer
{
    bool? myBool = true;
}

var bc = new BoolContainer();

const BindingFlags bindingFlags = BindingFlags.Public
                        | BindingFlags.NonPublic
                        | BindingFlags.Instance
                        ;


object obj;
object o = (object)bc;

foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
    obj = (object)fieldInfo.GetValue(o);
}

Bây giờ obj đề cập đến một đối tượng loại bool( System.Boolean) có giá trị bằng true. Những gì tôi thực sự muốn là một đối tượng của loạiNullable<bool>

Vì vậy, bây giờ với tư cách là một công việc xung quanh, tôi quyết định kiểm tra xem o là nullable và tạo một trình bao bọc nullable xung quanh obj.


Mã có nên bao gồm các chuỗi là nullable? Chúng là một ValueType không chung chung dường như là nullable. Hay chúng không phải là ValueType?
TamusJRoyce

Chuỗi không phải là ValueType. Đây là một loại tài liệu tham khảo.
Suncat2000

Đây thực sự là một câu hỏi tốt! 'Type.IsNullableType ()' là loại lừa dối vì thực tế nó chỉ kiểm tra loại là 'Nullable <T>', không trả về kết quả mong đợi nếu bạn thực sự muốn kiểm tra bất kỳ loại nào có thể chấp nhận null value (ví dụ: tôi đã cố sử dụng với a.IsNullableType (), trong đó 'a' là 'typeof (chuỗi)' được xác định khi chạy)
ErrCode

Câu trả lời nằm trong fieldInfo.FieldType: kiểm tra xem FieldType có phải là loại chung và loại chung là loại Nullable <> không. (Ví dụ: if (FieldType.IsGenericType && FieldType.GetGenericTypeDefDef () == typeof (Nullable <>))). Đừng cố lấy obj.GetType () nó sẽ có UndelyingSystemType của Nullable <T> biến T (trong trường hợp của bạn là loại Boolean, thay vì Nullable <Boolean>), đó là một vấn đề về quyền anh.
SoLaR

Câu trả lời:


271

Có hai loại nullable - Nullable<T>và loại tham chiếu.

Jon đã sửa cho tôi rằng thật khó để có được loại nếu được đóng hộp, nhưng bạn có thể với thuốc generic: - vậy làm thế nào về bên dưới. Đây thực sự là loại thử nghiệm T, nhưng sử dụng objtham số hoàn toàn cho suy luận kiểu chung (để dễ gọi) - objmặc dù vậy nó sẽ hoạt động gần như giống hệt nhau mà không cần tham số.

static bool IsNullable<T>(T obj)
{
    if (obj == null) return true; // obvious
    Type type = typeof(T);
    if (!type.IsValueType) return true; // ref-type
    if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
    return false; // value-type
}

Nhưng điều này sẽ không hoạt động tốt nếu bạn đã đóng hộp giá trị cho một biến đối tượng.

Tài liệu của Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type


7
Dòng cuối cùng chỉ có giá trị nếu bạn bằng cách nào đó quản lý để có được một Nullable <T> đóng hộp thay vì đấm bốc thẳng đến T. Điều đó là có thể, nhưng rất khó để đạt được từ những gì tôi nhớ.
Jon Skeet

Mã này hữu ích cho tôi, không phải vì tôi có Nullable <T> đóng hộp mà vì tôi đang viết một lớp cơ sở chuyển đổi WPF chung và một số thuộc tính là nullable, vì vậy tôi đã sử dụng Nullable.GetUnderellingType để phát hiện trường hợp đó và Activator.CreateInstance để thực hiện một hộp nullable, (Convert.ChangeType không xử lý nullables btw).
Qwertie

1
@ Tin nếu bạn muốn chỉnh sửa lại để làm rõ rằng anh ta đã không xem xét các loại tham chiếu, tôi nghĩ rằng câu trả lời của tôi có trước bản chỉnh sửa đó; người đọc có thể tự đưa ra quyết định của mình ở đó, dựa trên nhu cầu của chính họ, tôi nghi ngờ (đã xác nhận: nhận xét của anh ấy được giới thiệu lại vào lúc 14:42; câu trả lời của tôi là tất cả <= 14:34)
Marc Gravell

1
(Obj == null) sẽ ném ngoại lệ khi obj = 1?
Fan hâm mộ Qi

3
@JustinMorgan Nếu Tlà một tham số chung bị ràng buộc bởi T : struct, thì Tkhông được phép Nullable<>, vì vậy bạn không cần kiểm tra trong trường hợp đó! Tôi biết loại Nullable<>này là một cấu trúc, nhưng trong C #, ràng buộc where T : structđặc biệt loại trừ các loại giá trị nullable. Thông số kỹ thuật cho biết: "Lưu ý rằng mặc dù được phân loại là loại giá trị, loại không thể (§4.1.10) không thỏa mãn ràng buộc loại giá trị."
Jeppe Stig Nielsen

46

Có một giải pháp rất đơn giản là sử dụng phương thức quá tải

http://deanchalk.com/is-it-nullable/

đoạn trích:

public static class ValueTypeHelper
{
    public static bool IsNullable<T>(T t) { return false; }
    public static bool IsNullable<T>(T? t) where T : struct { return true; }
}

sau đó

static void Main(string[] args)
{
    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    bool result1 = ValueTypeHelper.IsNullable(a); // false
    bool result2 = ValueTypeHelper.IsNullable(b); // true
    bool result3 = ValueTypeHelper.IsNullable(c); // false
    bool result4 = ValueTypeHelper.IsNullable(d); // false
    bool result5 = ValueTypeHelper.IsNullable(e); // true
    bool result6 = ValueTypeHelper.IsNullable(f); // true

7
cộng với một cho bạn sir để thêm trường hợp thử nghiệm. Tôi đã sử dụng những trường hợp thử nghiệm để kiểm tra tất cả các câu trả lời khác. Nhiều người nên đi thêm chút này.
Marty Neal

4
Đối với những gì nó có giá trị, điều này không hoạt động trong VB.NET. Nó dẫn đến lỗi trình biên dịch " Độ phân giải quá tải không thành công vì không thể truy cập 'IsNullable' cụ thể nhất cho các đối số này " trong tất cả các tình huống Truesẽ được trả về.
ckittel

1
Tôi thực sự thích giải pháp này - và thật xấu hổ khi VB không thể xử lý nó. Tôi đã thử làm việc với ValueType nhưng gặp rắc rối với trình biên dịch VB không nhất quán về việc sử dụng quá tải dựa trên việc nó được gọi là phương thức chia sẻ hay tiện ích mở rộng, tôi thậm chí còn đặt ra một câu hỏi về điều này vì nó có vẻ kỳ lạ: stackoverflow.com/ câu hỏi / 12319591 / Hoài
James Đóng

22
Bạn đang kiểm tra loại thời gian biên dịch , nhưng nó đã rõ ràng (từ intellisense) nếu loại thời gian biên dịch là nullable ( System.Nullable<>). Nếu bạn nói object g = e;và sau đó ValueTypeHelper.IsNullable(g), bạn mong đợi gì để có được?
Jeppe Stig Nielsen

18
Tôi vừa xác minh; Điều này không hoạt động , như Jeppe nói. Nếu các biến được truyền tới đối tượng, nó sẽ luôn trả về false. Vì vậy, bạn không thể xác định loại của một đối tượng không xác định trong thời gian chạy theo cách này. Thời gian duy nhất hoạt động này là nếu loại được cố định tại thời gian biên dịch và trong trường hợp đó bạn không cần kiểm tra thời gian chạy.
HugoRune

30

Câu hỏi "Làm thế nào để kiểm tra nếu một loại là nullable?" thực ra là "Làm thế nào để kiểm tra xem một loại có phải Nullable<>không?", có thể được khái quát thành "Làm thế nào để kiểm tra xem một loại có phải là loại được xây dựng của một số loại chung không?", để nó không chỉ trả lời câu hỏi "Là Nullable<int>một Nullable<>?", mà còn "là List<int>một List<>?".

Hầu hết các giải pháp được cung cấp sử dụng Nullable.GetUnderlyingType()phương pháp, rõ ràng sẽ chỉ hoạt động với trường hợp Nullable<>. Tôi không thấy giải pháp phản xạ chung sẽ hoạt động với bất kỳ loại chung nào, vì vậy tôi quyết định thêm nó vào đây cho hậu thế, mặc dù câu hỏi này đã được trả lời từ lâu.

Để kiểm tra xem một loại có phải là một dạng Nullable<>sử dụng sự phản chiếu hay không, trước tiên bạn phải chuyển đổi loại chung được xây dựng của mình, ví dụ Nullable<int>, thành định nghĩa loại chung , Nullable<>. Bạn có thể làm điều đó bằng cách sử dụng GetGenericTypeDefinition()phương thức của Typelớp. Sau đó, bạn có thể so sánh loại kết quả với Nullable<>:

Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true

Điều tương tự có thể được áp dụng cho bất kỳ loại chung chung:

Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

Một số loại có vẻ giống nhau, nhưng một số đối số loại khác nhau có nghĩa là nó là một loại hoàn toàn khác.

Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false

Typeđối tượng được khởi tạo một lần cho mỗi loại, bạn có thể kiểm tra sự bằng nhau tham chiếu giữa chúng. Vì vậy, nếu bạn muốn kiểm tra xem hai đối tượng có cùng định nghĩa kiểu chung hay không, bạn có thể viết:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

Nếu bạn muốn kiểm tra xem một đối tượng có thể là null hay không Type, thì bạn có thể sử dụng kỹ thuật trên cùng với giải pháp của Marc Gravell để tạo ra một phương thức khá đơn giản:

static bool IsNullable<T>(T obj)
{
    if (!typeof(T).IsGenericType)
        return false;

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}

@ AllonGuralnek Có phiên bản đơn giản hóa trong câu trả lời của tôi. Tôi muốn làm cho nó thành chỉnh sửa và vì danh tiếng của tôi không phải là cấp độ của bạn, nó sẽ được chỉnh sửa mà không có tên của tôi trong câu trả lời của bạn, mặc dù vậy, có vẻ như đánh giá luôn luôn bắn vào chân tôi, rằng đó là tác giả tôn trọng ngay cả khi nó là không phải. Thế giới kỳ lạ, một số người không nhận được định nghĩa :).
ipavlu

@ipavlu: Phiên bản của bạn không được đơn giản hóa, thực tế nó phức tạp hơn. Tôi nghĩ rằng bạn có nghĩa là nó được tối ưu hóa kể từ khi bạn lưu trữ kết quả. Điều đó làm cho nó khó hiểu hơn.
Allon Guralnek

@ AllonGuralnek lớp chung tĩnh và các trường khởi tạo tĩnh một lần, điều đó có phức tạp không? Lạy Chúa, tôi đã phạm tội khủng khiếp :).
ipavlu

@ipavku: Có, bởi vì nó không liên quan gì đến câu hỏi "Làm thế nào để kiểm tra xem một đối tượng có bị vô hiệu không?". Tôi cố gắng làm cho nó đơn giản và đi vào vấn đề, và tôi tránh đưa ra các khái niệm không cần thiết và không liên quan.
Allon Guralnek

1
@nawfal: Nếu tôi hiểu bạn một cách chính xác, việc bạn thực hiện nhiệm vụ của tôi khi đối mặt với sự tồn tại của Nullable.GetUnderlyingType()khung đã được cung cấp. Tại sao không chỉ sử dụng phương thức trong khung? Bạn cũng nên. Nó rõ ràng hơn, súc tích hơn và thử nghiệm tốt hơn. Nhưng trong bài viết của tôi, tôi đang cố gắng dạy cách sử dụng sự phản chiếu để có được thông tin bạn muốn, để ai đó có thể áp dụng nó cho bất kỳ loại nào (bằng cách thay thế typeof(Nullable<>)bằng bất kỳ loại nào khác). Nếu bạn nhìn vào các nguồn của GetUnderlyingType()(bản gốc hoặc dịch ngược), bạn sẽ thấy nó rất giống với mã của tôi.
Allon Guralnek

30

Điều này làm việc cho tôi và có vẻ đơn giản:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

Đối với loại giá trị:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}

7
Để biết giá trị của nó, đây cũng là thử nghiệm được Microsoft sử dụng
canton7

1
Đẹp ... Đây không phải là câu trả lời hàng đầu vì nó đến sau? Tôi tìm thấy câu trả lời hàng đầu rất khó hiểu.
Vincent Buscarello

1
Đây phải là câu trả lời hàng đầu. Sau nhiều ngày thử các phương pháp khác nhau, tôi đã nghĩ ngẫu nhiên về giải pháp này, đã thử nó và dường như nó hoạt động hoàn hảo (so với câu trả lời được xếp hạng cao nhất)
user3163495

2
Đây là một giải pháp tuyệt vời để tìm hiểu xem có thể đặt bất kỳ trường hợp nào thành NULL hay không, nhưng nó sẽ trả về đúng cho mọi thứ có thể được đặt thành null, bao gồm các đối tượng thông thường. Điều quan trọng là phải nhận ra rằng câu hỏi ban đầu đặc biệt muốn phát hiện Nullable ValueTypes.
JamesHoux

20

Vâng, bạn có thể sử dụng:

return !(o is ValueType);

... nhưng bản thân một đối tượng không thể rỗng hoặc nói cách khác - một loại là. Làm thế nào bạn có kế hoạch sử dụng này?


2
Điều này đã ném tôi đi một chút. vd i = 5; typeof (i) trả về System.Int32 thay vì Nullable <Int32> - typeof (int?) trả về Nullable <Int32> .. tôi có thể hiểu rõ hơn về chủ đề này ở đâu?
Gishu

2
typeof (i) sẽ đưa ra lỗi trình biên dịch - bạn không thể sử dụng typeof với một biến. Bạn thực sự đã làm gì?
Jon Skeet

15
i.GetType () sẽ đóng hộp vào Object trước và không có loại nào là loại nullable được đóng hộp - Nullable <int> được đóng hộp vào tham chiếu null hoặc int được đóng hộp.
Jon Skeet

Cách đó tốt hơn Nullable.GetUnderellingType (loại)! = Null?
Kiquenet

@Kiquenet: Chúng tôi không loại ở đây - chỉ là giá trị.
Jon Skeet

11

Cách đơn giản nhất tôi có thể tìm ra là:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}

+1. Giải pháp tuyệt vời cho các loại null-box đóng hộp. Tôi chưa thử nghiệm điều này cụ thể. Vì vậy, nếu bất cứ ai khác có thể xác minh, nó sẽ được đánh giá cao.
TamusJRoyce

Tôi đã thử nó rồi. Tôi đã phải tạo ra một loại Nullable, nhưng với ngữ nghĩa khác nhau. Trong tình huống của tôi, tôi nên hỗ trợ nullnhư một giá trị hợp lệ và cũng không hỗ trợ giá trị nào cả. Vì vậy, một loại tạo ra Optional. Vì cần phải hỗ trợ nullcác giá trị, tôi cũng phải triển khai mã để xử lý Nullablecác giá trị như là một phần của việc triển khai. Đó là nơi mã này đến từ.
CARLOS LÔ

9
Tôi nghĩ rằng giải pháp này là sai. Truyền một loại giá trị Nullable làm đối số cho một phương thức mong đợi một tham số của loại đối tượng sẽ khiến quyền anh xảy ra. Nullable là một loại giá trị và kết quả của chuyển đổi quyền anh là một loại tham chiếu. Không có nullables đóng hộp. Tôi tin rằng phương pháp này luôn trả về sai?
Mishax

1
Bất kỳ bài kiểm tra về nó như câu trả lời khác?
Kiquenet

5
Nó không hoạt động vì giá trị đấm bốc. Nó sẽ luôn luôn trả về SAI.
N Rocking

10

Có hai vấn đề ở đây: 1) thử nghiệm để xem liệu Loại có thể không; và 2) thử nghiệm để xem liệu một đối tượng đại diện cho Loại không thể.

Đối với sự cố 1 (kiểm tra Loại), đây là giải pháp tôi đã sử dụng trong các hệ thống của riêng mình: Giải pháp kiểm tra TypeIsNullable-check

Đối với vấn đề 2 (kiểm tra một đối tượng), giải pháp của Dean Powder ở trên hoạt động đối với các loại giá trị, nhưng nó không hoạt động đối với các loại tham chiếu, vì sử dụng quá tải <T> luôn trả về sai. Vì các loại tham chiếu vốn đã vô hiệu, nên việc kiểm tra một loại tham chiếu sẽ luôn trả về giá trị true. Vui lòng xem ghi chú [Giới thiệu về "nullable"] bên dưới để được giải thích về các ngữ nghĩa này. Vì vậy, đây là sửa đổi của tôi đối với cách tiếp cận của Dean:

    public static bool IsObjectNullable<T>(T obj)
    {
        // If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
        if (!typeof(T).IsValueType || obj == null)
            return true;

        // Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
        return false; 
    }

    public static bool IsObjectNullable<T>(T? obj) where T : struct
    {
        // Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
        return true;
    }

Và đây là sửa đổi của tôi đối với mã kiểm tra máy khách cho giải pháp trên:

    int a = 123;
    int? b = null;
    object c = new object();
    object d = null;
    int? e = 456;
    var f = (int?)789;
    string g = "something";

    bool isnullable = IsObjectNullable(a); // false 
    isnullable = IsObjectNullable(b); // true 
    isnullable = IsObjectNullable(c); // true 
    isnullable = IsObjectNullable(d); // true 
    isnullable = IsObjectNullable(e); // true 
    isnullable = IsObjectNullable(f); // true 
    isnullable = IsObjectNullable(g); // true

Lý do tôi đã sửa đổi cách tiếp cận của Dean trong IsObjectNullable <T> (T t) là cách tiếp cận ban đầu của anh ta luôn trả về sai cho loại tham chiếu. Vì một phương thức như IsObjectNullable sẽ có thể xử lý các giá trị loại tham chiếu và vì tất cả các loại tham chiếu đều là null, nên nếu một kiểu tham chiếu hoặc null được truyền, thì phương thức sẽ luôn trả về đúng.

Hai phương thức trên có thể được thay thế bằng phương thức đơn sau đây và đạt được cùng một đầu ra:

    public static bool IsObjectNullable<T>(T obj)
    {
        Type argType = typeof(T);
        if (!argType.IsValueType || obj == null)
            return true;
        return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

Tuy nhiên, vấn đề với cách tiếp cận phương thức đơn cuối cùng này là hiệu năng bị ảnh hưởng khi sử dụng tham số Nullable <T>. Phải mất nhiều thời gian xử lý hơn để thực thi dòng cuối cùng của phương thức này so với việc cho phép trình biên dịch chọn quá tải phương thức thứ hai được hiển thị trước đó khi tham số Nullable <T> -type được sử dụng trong lệnh gọi IsObjectNullable. Do đó, giải pháp tối ưu là sử dụng phương pháp hai phương pháp được minh họa ở đây.

CAVEAT: Phương thức này chỉ hoạt động đáng tin cậy nếu được gọi bằng cách sử dụng tham chiếu đối tượng ban đầu hoặc một bản sao chính xác, như trong các ví dụ. Tuy nhiên, nếu một đối tượng nullable được đóng hộp vào Loại khác (chẳng hạn như đối tượng, v.v.) thay vì còn lại ở dạng Nullable <> ban đầu, phương thức này sẽ không hoạt động đáng tin cậy. Nếu mã gọi phương thức này không sử dụng tham chiếu đối tượng gốc, không có hộp hoặc một bản sao chính xác, thì nó không thể xác định một cách đáng tin cậy tính vô hiệu của đối tượng bằng phương thức này.

Trong hầu hết các kịch bản mã hóa, để xác định tính không có giá trị, người ta phải dựa vào việc kiểm tra Loại của đối tượng ban đầu, chứ không phải tham chiếu của nó (ví dụ: mã phải có quyền truy cập vào Loại ban đầu của đối tượng để xác định tính không hợp lệ). Trong những trường hợp phổ biến hơn này, IsTypeNullable (xem liên kết) là một phương pháp đáng tin cậy để xác định tính không hợp lệ.

PS - Giới thiệu về "nullable"

Tôi nên lặp lại một tuyên bố về sự vô hiệu mà tôi đã thực hiện trong một bài đăng riêng biệt, áp dụng trực tiếp để giải quyết đúng chủ đề này. Đó là, tôi tin rằng trọng tâm của cuộc thảo luận ở đây không phải là làm thế nào để kiểm tra xem một đối tượng có phải là loại Nullable chung hay không, mà là liệu người ta có thể gán giá trị null cho một đối tượng thuộc loại đó không. Nói cách khác, tôi nghĩ rằng chúng ta nên xác định xem một loại đối tượng là nullable, chứ không phải là Nullable. Sự khác biệt là về ngữ nghĩa, cụ thể là lý do thực tế để xác định tính vô hiệu, thường là tất cả những gì quan trọng.

Trong một hệ thống sử dụng các đối tượng với các loại có thể không xác định cho đến thời gian chạy (dịch vụ web, cuộc gọi từ xa, cơ sở dữ liệu, nguồn cấp dữ liệu, v.v.), một yêu cầu chung là xác định xem null có thể được gán cho đối tượng hay không hoặc liệu đối tượng có thể chứa một con số không Việc thực hiện các hoạt động như vậy trên các loại không có giá trị sẽ có thể tạo ra lỗi, thường là các trường hợp ngoại lệ, rất tốn kém cả về hiệu suất và yêu cầu mã hóa. Để thực hiện phương pháp được ưu tiên cao là chủ động tránh các vấn đề như vậy, cần xác định xem một đối tượng của Loại tùy ý có khả năng chứa null hay không; tức là, cho dù đó là "không thể".

Theo một nghĩa rất thực tế và điển hình, tính vô hiệu trong các thuật ngữ .NET hoàn toàn không ngụ ý rằng Loại của đối tượng là một dạng Nullable. Trong nhiều trường hợp trên thực tế, các đối tượng có các kiểu tham chiếu, có thể chứa giá trị null và do đó tất cả đều là null; không ai trong số này có loại Nullable. Do đó, đối với các mục đích thực tế trong hầu hết các kịch bản, thử nghiệm nên được thực hiện cho khái niệm chung về tính không có giá trị, so với khái niệm phụ thuộc vào việc thực hiện của Nullable. Vì vậy, chúng ta không nên gác máy bằng cách chỉ tập trung vào loại .NET Nullable mà nên kết hợp sự hiểu biết của chúng ta về các yêu cầu và hành vi của nó trong quá trình tập trung vào khái niệm chung, thực tế về tính vô hiệu.


8

Giải pháp đơn giản nhất mà tôi đã đưa ra là triển khai giải pháp của Microsoft ( Cách: Xác định loại không thể (Hướng dẫn lập trình C #) ) như một phương pháp mở rộng:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

Điều này sau đó có thể được gọi như vậy:

bool isNullable = typeof(int).IsNullable();

Đây cũng có vẻ là một cách hợp lý để truy cập IsNullable()vì nó phù hợp với tất cả các IsXxxx()phương thức khác của Typelớp.


1
Bạn không muốn sử dụng "==" thay vì "! ="?
vkelman

Điểm tốt @vkelman Thay vì thực hiện thay đổi đó, tôi đã cập nhật câu trả lời để sử dụng đề xuất hiện tại từ Microsoft vì điều này đã thay đổi kể từ khi tôi viết bài này.
sclarke81

6

Hãy cẩn thận, khi đấm bốc một loại nullable ( Nullable<int>hoặc int? Chẳng hạn):

int? nullValue = null;
object boxedNullValue = (object)nullValue;
Debug.Assert(boxedNullValue == null);

int? value = 10;
object boxedValue = (object)value;
Debug.Assert( boxedValue.GetType() == typeof(int))

Nó trở thành một kiểu tham chiếu thực sự, vì vậy bạn mất đi sự thật là nó không thể.


3

Có thể một chút lạc đề, nhưng vẫn còn một số thông tin thú vị. Tôi tìm thấy rất nhiều người sử dụng Nullable.GetUnderlyingType() != nullđể nhận dạng nếu một loại là nullable. Điều này rõ ràng hoạt động, nhưng Microsoft khuyên những điều sau đây type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)(xem http://msdn.microsoft.com/en-us/l Library / ms366789.aspx ).

Tôi đã xem xét điều này từ một khía cạnh hiệu suất. Kết luận của thử nghiệm (một triệu lần thử) bên dưới là khi một loại là không thể, tùy chọn Microsoft mang lại hiệu suất tốt nhất.

Nullable.GetUnderellingType (): 1335ms (chậm hơn 3 lần)

GetGenericTypeDefDef () == typeof (Không thể <>): 500ms

Tôi biết rằng chúng ta đang nói về một lượng nhỏ thời gian, nhưng mọi người đều thích điều chỉnh mili giây :-)! Vì vậy, nếu ông chủ của bạn muốn bạn giảm một vài phần nghìn giây thì đây là vị cứu tinh của bạn ...

/// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
    int attempts = 1000000;

    Type nullableType = typeof(Nullable<int>);

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
    {
        Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable"); 
    }

    Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);

    stopwatch.Restart();

    for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
   {
       Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
   }

   Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
   stopwatch.Stop();
}

1
Xin chào, có lẽ có một vấn đề với việc đo thời gian, Assert có thể ảnh hưởng đến kết quả. Bạn đã thử nghiệm mà không khẳng định? Ngoài ra Console.WriteLine nên ở ngoài vùng đồng hồ đo. +1 cho một nỗ lực để định lượng các vấn đề hiệu suất :).
ipavlu

@ipavlu Console.WriteLinethực sự nằm ngoài khu vực đồng hồ đo;)
nawfal

Roel, như ipavlu đã đề cập, Assertnên ở ngoài vòng lặp. Thứ hai, bạn cũng nên kiểm tra nó với các giá trị không nullable để kiểm tra các trường hợp sai. Tôi đã thực hiện một thử nghiệm tương tự (2 nulables và 4 non-nullables) và tôi nhận được ~ 2 giây trong GetUnderlyingTypevà ~ 1 giây cho GetGenericTypeDefinition, tức GetGenericTypeDefinitionlà nhanh hơn hai lần (không phải ba lần).
nawfal

Đã làm một vòng khác với 2 nullables và 2 non-nullables - GetUnderlyingTypelần này chậm hơn 2,5 lần. Chỉ với những thứ không có giá trị - lần này cả hai đều là cổ và cổ.
nawfal

Nhưng quan trọng hơn, GetUnderlyingTyperất hữu ích khi bạn phải kiểm tra tính không hợp lệ và nhận loại cơ bản nếu nó là nullable. Điều này rất hữu ích và bạn thấy các mẫu thường thích Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). Nó giống như astừ khóa, kiểm tra diễn viên cũng như kết quả và trả về. Nếu bạn muốn lấy lại loại nullable cơ bản thì thực hiện GetGenericTypeDefinitionkiểm tra và sau đó lấy loại chung sẽ là một ý tưởng tồi. Cũng GetUnderlyingTypedễ đọc hơn nhiều & đáng nhớ. Tôi sẽ không phiền nếu tôi chỉ làm ~ 1000 lần.
nawfal

0

Phiên bản này:

  • kết quả bộ nhớ đệm nhanh hơn,
  • không yêu cầu các biến không cần thiết, như Phương thức (T obj)
  • KHÔNG PHỨC TẠP :),
  • chỉ là lớp chung tĩnh, có các trường được tính toán một lần

:

public static class IsNullable<T>
{
    private static readonly Type type = typeof(T);
    private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    public static bool Result { get { return is_nullable; } }
}

bool is_nullable = IsNullable<int?>.Result;

Tôi nghĩ rằng bạn đã tự trả lời chính mình bằng tuyên bố tĩnh 'is_nullable'. Mẹo: khai báo các đối tượng với int? (đối tượng a = (int?) 8;) và xem điều gì xảy ra.
SoLaR

0

Đây là những gì tôi nghĩ ra, vì mọi thứ khác dường như thất bại - ít nhất là trên PLC - Thư viện lớp di động / .NET Core với> = C # 6

Giải pháp: Mở rộng phương pháp tĩnh cho bất kỳ loại TNullable<T>và sử dụng thực tế là phương pháp khuyến nông tĩnh, phù hợp với các loại cơ bản sẽ được gọi và được ưu tiên hơn các chung Tmở rộng-method.

Dành cho T :

public static partial class ObjectExtension
{
    public static bool IsNullable<T>(this T self)
    {
        return false;
    }
}

va cho Nullable<T>

public static partial class NullableExtension
{
    public static bool IsNullable<T>(this Nullable<T> self) where T : struct
    {
        return true;
    }
}

Sử dụng Reflection và type.IsGenericType... không hoạt động trên tập hợp .NET Runtimes hiện tại của tôi. Tài liệu MSDN cũng không giúp được gì.

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

Một phần vì API Reflection đã được thay đổi khá đáng kể trong .NET Core.


0

Tôi nghĩ rằng những ứng dụng sử dụng thử nghiệm được đề xuất của Microsoft IsGenericTypelà tốt, nhưng trong mã cho GetUnderlyingType, Microsoft sử dụng thử nghiệm bổ sung để đảm bảo bạn không vượt qua định nghĩa loại chung Nullable<>:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));

-1

một cách đơn giản để làm điều này:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

đây là những bài kiểm tra đơn vị của tôi và tất cả đã qua

    IsNullable_String_ShouldReturn_True
    IsNullable_Boolean_ShouldReturn_False
    IsNullable_Enum_ShouldReturn_Fasle
    IsNullable_Nullable_ShouldReturn_True
    IsNullable_Class_ShouldReturn_True
    IsNullable_Decimal_ShouldReturn_False
    IsNullable_Byte_ShouldReturn_False
    IsNullable_KeyValuePair_ShouldReturn_False

kiểm tra đơn vị thực tế

    [TestMethod]
    public void IsNullable_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Boolean_ShouldReturn_False()
    {
        var typ = typeof(bool);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Enum_ShouldReturn_Fasle()
    {
        var typ = typeof(System.GenericUriParserOptions);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Nullable_ShouldReturn_True()
    {
        var typ = typeof(Nullable<bool>);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Class_ShouldReturn_True()
    {
        var typ = typeof(TestPerson);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Decimal_ShouldReturn_False()
    {
        var typ = typeof(decimal);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Byte_ShouldReturn_False()
    {
        var typ = typeof(byte);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_KeyValuePair_ShouldReturn_False()
    {
        var typ = typeof(KeyValuePair<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.