จะตรวจสอบว่าวัตถุนั้นเป็นโมฆะได้อย่างไร?


202

ฉันจะตรวจสอบว่าวัตถุที่กำหนดเป็นโมฆะในคำอื่น ๆ วิธีการใช้วิธีการดังต่อไปนี้ ...

bool IsNullableValueType(object o)
{
    ...
}

แก้ไข: ฉันกำลังมองหาประเภทค่าที่เป็นโมฆะ ฉันไม่มีประเภทอ้างอิง

//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);
}

obj ตอนนี้หมายถึงวัตถุของการพิมพ์bool( System.Boolean) trueมีค่าเท่ากับ สิ่งที่ฉันต้องการจริงๆคือประเภทของวัตถุNullable<bool>

ดังนั้นในขณะที่การทำงานรอบ ๆ ฉันตัดสินใจที่จะตรวจสอบว่า o เป็นโมฆะและสร้างเสื้อคลุม nullable รอบ obj


รหัสควรรวมสตริงเป็นโมฆะหรือไม่ มันเป็น ValueType ที่ไม่ใช่แบบทั่วไปซึ่งดูเหมือนจะเป็นโมฆะ หรือพวกเขาไม่ใช่ ValueType?
TamusJRoyce

สตริงไม่ใช่ ValueType มันเป็นประเภทอ้างอิง
Suncat2000

นี่เป็นคำถามที่ดีจริงๆ! 'Type.IsNullableType ()' เป็นการหลอกลวงเพราะตรวจสอบเฉพาะประเภทที่เป็น 'Nullable <T>' ซึ่งไม่ได้ส่งคืนผลลัพธ์ที่คาดหวังหากคุณต้องการตรวจสอบประเภทที่ยอมรับ null ค่า (เช่นฉันพยายามใช้กับ a.IsNullableType () โดยที่ 'a' คือ 'typeof (string)' กำหนดไว้ที่ runtime)
ErrCode

คำตอบอยู่ใน fieldInfo.FieldType: ตรวจสอบว่า FieldType เป็นประเภททั่วไปและประเภททั่วไปเป็นชนิด Nullable <> (ตัวอย่าง: ถ้า (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))) อย่าพยายาม obj.GetType () มันจะมี UndelyingSystemType ของตัวแปร Nullable <T> T (ในกรณีของคุณเป็นประเภท Boolean แทนที่จะเป็น Nullable <Boolean>) นั่นเป็นปัญหาเรื่องมวย
SoLaR

คำตอบ:


271

มีค่า null สองประเภท - Nullable<T>และชนิดอ้างอิง

จอนแก้ไขให้ฉันด้วยว่ามันยากที่จะพิมพ์ถ้าเป็นแบบกล่อง แต่คุณสามารถใช้ชื่อสามัญได้: - แล้วด้านล่างล่ะ นี่เป็นการทดสอบประเภทจริง ๆTแต่การใช้objพารามิเตอร์ล้วนสำหรับการอนุมานประเภททั่วไป (เพื่อให้ง่ายต่อการโทร) - มันจะทำงานเกือบเหมือนกันโดยไม่มีobjพารามิเตอร์แม้ว่า

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
}

แต่สิ่งนี้จะทำงานได้ไม่ดีนักหากคุณได้ใส่ค่าลงในตัวแปร object แล้ว

เอกสารของ Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type


7
บรรทัดสุดท้ายจะใช้ได้ก็ต่อเมื่อคุณจัดการเพื่อรับ Nullable ชนิดบรรจุกล่องแทนการชกมวยตรงไปที่ T เป็นไปได้ แต่เป็นเรื่องยากที่จะบรรลุจากสิ่งที่ฉันจำได้
Jon Skeet

รหัสนี้มีประโยชน์สำหรับฉันไม่ใช่เพราะฉันได้รับ Nullable <T> ชนิดบรรจุกล่อง แต่เป็นเพราะฉันเขียนคลาสพื้นฐานตัวแปลง WPF ทั่วไปและคุณสมบัติบางอย่างนั้นเป็นโมฆะดังนั้นฉันจึงใช้ Nullable.GetUnderlyingType เพื่อตรวจสอบเคสและ Activator.CreateInstance กล่องเป็นโมฆะ nullable (แปลง. ChangeType ไม่จัดการ nullables btw)
Qwertie

1
@Abel ถ้าคุณหมายถึงการแก้ไขของเขาอีกครั้งเพื่อให้ความกระจ่างว่าเขาไม่ได้พิจารณาประเภทการอ้างอิงฉันคิดว่าคำตอบของฉันได้รับการแก้ไขแล้ว ผู้อ่านสามารถทำการตัดสินใจของตัวเองที่นั่นตามความต้องการของตัวเองฉันสงสัย (ยืนยัน: ความคิดเห็นของเขาเป็นประเภทอ้างอิงที่เพิ่มเมื่อเวลา 14:42 น. คำตอบของฉันคือ <= 14:34)
Marc Gravell

1
(obj == null) จะส่งข้อยกเว้นเมื่อ obj = 1 หรือไม่
Qi Fan

3
@JustinMorgan ถ้าTเป็นพารามิเตอร์ที่ จำกัด โดยทั่วไปT : structแล้วTจะไม่ได้รับอนุญาตให้เป็นNullable<>ดังนั้นคุณต้องตรวจสอบในกรณีที่ไม่! ฉันรู้ว่าประเภทNullable<>เป็น struct แต่ใน C # ข้อ จำกัดwhere T : structเฉพาะไม่รวมค่าประเภท nullable ข้อมูลจำเพาะระบุว่า: "โปรดทราบว่าแม้ว่าจำแนกเป็นประเภทค่าได้แล้ว แต่ประเภท nullable (§4.1.10) ไม่เป็นไปตามข้อ จำกัด ประเภทค่า"
Jeppe Stig Nielsen

46

มีวิธีแก้ปัญหาที่ง่ายมากโดยใช้เมธอดโอเวอร์โหลด

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

ตัดตอนมา:

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; }
}

แล้วก็

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
บวกหนึ่งสำหรับคุณที่เพิ่มกรณีทดสอบ ฉันใช้กรณีทดสอบเหล่านั้นเพื่อตรวจสอบคำตอบอื่น ๆ ทั้งหมด ผู้คนมากขึ้นควรที่จะเพิ่มบิตนี้
Marty Neal

4
สำหรับสิ่งที่คุ้มค่านี่ใช้ไม่ได้กับ VB.NET มันส่งผลให้เกิดข้อผิดพลาดของคอมไพเลอร์ของ "การแก้ปัญหาโอเวอร์โหลดล้มเหลวเนื่องจากไม่สามารถเข้าถึง 'IsNullable' ได้เฉพาะเจาะจงที่สุดสำหรับอาร์กิวเมนต์เหล่านี้ " ในทุกสถานการณ์ที่Trueจะถูกส่งคืน
ckittel

1
ฉันชอบโซลูชันนี้ - และมันเป็นความอัปยศ VB ไม่สามารถจัดการได้ ฉันพยายามทำงานกับ ValueType แต่พบปัญหากับคอมไพเลอร์ VB ที่ไม่สอดคล้องกันเกี่ยวกับโอเวอร์โหลดที่จะใช้โดยขึ้นอยู่กับว่ามันถูกเรียกว่าเป็นวิธีการแชร์หรือส่วนขยายฉันยังตั้งคำถามเกี่ยวกับเรื่องนี้เพราะมันแปลก: stackoverflow.com/ คำถาม / 12319591 / …
James ปิด

22
คุณกำลังตรวจสอบประเภทเวลาคอมไพล์แต่ชัดเจนแล้ว (จาก Intellisense) หากประเภทเวลาคอมไพล์เป็นโมฆะ ( System.Nullable<>) ถ้าคุณพูดobject g = e;แล้วValueTypeHelper.IsNullable(g)คุณคาดหวังอะไร
Jeppe Stig Nielsen

18
ฉันเพิ่งตรวจสอบ สิ่งนี้ไม่ได้ผลอย่างที่ Jeppe พูด หากตัวแปรนั้นถูกส่งไปยังวัตถุมันจะส่งคืนค่าเท็จเสมอ ดังนั้นคุณจึงไม่สามารถระบุประเภทของวัตถุที่ไม่รู้จักที่ใช้งานด้วยวิธีนี้ ครั้งเดียวที่ใช้งานได้คือถ้าประเภทได้รับการแก้ไขในเวลารวบรวมและในกรณีนั้นคุณไม่จำเป็นต้องตรวจสอบรันไทม์เลย
HugoRune

30

คำถามของ "วิธีการตรวจสอบว่าประเภทเป็นโมฆะ?" เป็นจริง "วิธีการตรวจสอบว่าเป็นประเภทNullable<>" ซึ่งสามารถ generalized เพื่อ "วิธีการตรวจสอบว่าเป็นประเภททั่วไปประเภทสร้าง?" เพื่อที่จะไม่เพียง แต่ตอบคำถาม " Nullable<int>เป็นNullable<>?" แต่ยัง " List<int>เป็นList<>หรือไม่"

โซลูชันที่ให้มาส่วนใหญ่ใช้Nullable.GetUnderlyingType()วิธีการซึ่งเห็นได้ชัดว่าใช้ได้กับกรณีของNullable<>เท่านั้น ฉันไม่เห็นโซลูชันไตร่ตรองทั่วไปที่จะใช้ได้กับประเภททั่วไปใด ๆ ดังนั้นฉันตัดสินใจเพิ่มที่นี่เพื่อลูกหลานแม้ว่าคำถามนี้จะได้รับคำตอบมานานแล้ว

เพื่อตรวจสอบว่าเป็นชนิดที่เป็นรูปแบบของบางอย่างNullable<>ใช้สะท้อนอันดับแรกคุณต้องแปลงประเภททั่วไปของคุณสร้างขึ้นเช่นลงไปในการกำหนดประเภททั่วไปNullable<int> Nullable<>คุณสามารถทำได้โดยใช้GetGenericTypeDefinition()วิธีการTypeเรียน จากนั้นคุณสามารถเปรียบเทียบประเภทผลลัพธ์กับNullable<>:

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

สามารถนำไปใช้กับประเภททั่วไปใด ๆ :

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

หลายประเภทอาจดูเหมือนกัน แต่จำนวนอาร์กิวเมนต์ประเภทที่แตกต่างกันหมายความว่าเป็นประเภทที่แตกต่างอย่างสิ้นเชิง

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วัตถุนั้นถูกสร้างขึ้นหนึ่งครั้งต่อประเภทคุณสามารถตรวจสอบความเท่าเทียมกันของการอ้างอิงระหว่างพวกเขาได้ ดังนั้นหากคุณต้องการตรวจสอบว่าวัตถุสองรายการนั้นมีคำจำกัดความชนิดเดียวกันหรือไม่คุณสามารถเขียน:

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

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

หากคุณต้องการตรวจสอบว่าวัตถุนั้นเป็นโมฆะหรือไม่Typeคุณสามารถใช้เทคนิคข้างต้นร่วมกับโซลูชันของ Marc Gravell เพื่อสร้างวิธีการที่ค่อนข้างง่าย:

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

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

@ AllonGuralnek มีคำแปลที่ง่ายกว่าในคำตอบของฉัน ฉันต้องการทำให้การแก้ไขเป็นและเนื่องจากชื่อเสียงของฉันไม่ได้เป็นระดับของคุณมันจะแก้ไขโดยไม่มีชื่อของฉันในคำตอบของคุณดังนั้นแม้ดูเหมือนว่าการตรวจสอบอยู่เสมอยิงของฉันในขาว่ามันเป็นผู้เขียนแม้ว่ามัน ไม่. โลกที่แปลกประหลาดบางคนไม่ได้คำจำกัดความ :)
ipavlu

@ipavlu: เวอร์ชันของคุณไม่ง่าย แต่อันที่จริงแล้วมันซับซ้อนกว่า ฉันคิดว่าคุณหมายถึงมันได้รับการปรับปรุงตั้งแต่คุณแคชผลลัพธ์ ทำให้ยากต่อการเข้าใจ
Allon Guralnek

@ AllonGuralnek คงที่ชั้นเรียนทั่วไปและคงที่ครั้งเดียวเขตข้อมูลเริ่มต้นที่มีความซับซ้อน? พระเจ้าที่รักฉันทำผิดร้ายแรง :)
ipavlu

@ipavku: ใช่เพราะมันไม่มีส่วนเกี่ยวข้องกับคำถาม "จะตรวจสอบว่าวัตถุนั้นเป็นโมฆะได้อย่างไร?" ฉันพยายามทำให้มันง่ายและตรงประเด็นและฉันหลีกเลี่ยงการแนะนำแนวคิดที่ไม่จำเป็นและไม่เกี่ยวข้อง
Allon Guralnek

1
@nawfal: ถ้าฉันเข้าใจคุณอย่างถูกต้องการแสวงหาการดำเนินการของฉันในการเผชิญกับการดำรงอยู่ของNullable.GetUnderlyingType()ที่ได้รับการจัดเตรียมโดยกรอบ ทำไมไม่ใช้วิธีนี้ในกรอบ? คุณควร มีความชัดเจนรัดกุมและทดสอบได้ดีกว่า แต่ในโพสต์ของฉันฉันพยายามสอนวิธีใช้การไตร่ตรองเพื่อให้ได้ข้อมูลที่คุณต้องการเพื่อให้ใครบางคนสามารถนำไปใช้กับประเภทtypeof(Nullable<>)ใดก็ได้ ถ้าคุณดูที่แหล่งที่มาของGetUnderlyingType()(ต้นฉบับหรือ decompiled) คุณจะเห็นมันเป็นมากคล้ายกับรหัสของฉัน
Allon Guralnek

30

มันใช้งานได้สำหรับฉันและดูเหมือนง่าย:

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

สำหรับประเภทค่า:

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

7
สำหรับสิ่งที่คุ้มค่านี่คือการทดสอบที่ใช้โดย Microsoft
canton7

1
ดี ... นี่ไม่ใช่คำตอบที่ดีที่สุดที่จะมาภายหลังหรือไม่ ฉันพบคำตอบที่ทำให้สับสนมาก
Vincent Buscarello

1
นี่ควรเป็นคำตอบที่ดีที่สุด หลังจากลองใช้วิธีการที่แตกต่างกันหลายวันฉันก็คิดถึงวิธีนี้ลองใช้มันและดูเหมือนว่ามันจะทำงานได้อย่างสมบูรณ์แบบ (เมื่อเทียบกับคำตอบที่ติดอันดับสูงสุด)
user3163495

2
นี่เป็นวิธีแก้ปัญหาที่ยอดเยี่ยมในการค้นหาว่าอินสแตนซ์ใด ๆ สามารถตั้งค่าเป็น NULL ได้หรือไม่ แต่จะคืนค่าจริงสำหรับทุกสิ่งที่สามารถตั้งค่าเป็นโมฆะรวมถึงวัตถุทั่วไป สิ่งสำคัญคือต้องตระหนักว่าคำถามเดิมต้องการตรวจจับ ValueTypes ที่มีค่าเป็น Nullable โดยเฉพาะ
JamesHoux

20

คุณสามารถใช้:

return !(o is ValueType);

... แต่ตัววัตถุเองนั้นไม่สามารถเป็นโมฆะหรืออย่างอื่น - ชนิดคือ คุณวางแผนที่จะใช้สิ่งนี้อย่างไร


2
เรื่องนี้ทำให้ฉันผิดหวัง เช่น int? ฉัน = 5; typeof (i) ส่งคืน System.Int32 แทน Nullable <Int32> - typeof (int?) ส่งคืน Nullable <Int32> .. ฉันจะรับความกระจ่างในหัวข้อนี้ได้ที่ไหน
Gishu

2
typeof (i) จะให้คอมไพเลอร์ผิดพลาด - คุณไม่สามารถใช้ typeof กับตัวแปรได้ คุณทำอะไรจริงๆ
Jon Skeet

15
i.GetType () จะส่งไปที่ Object ก่อนและไม่มีสิ่งใดเป็นประเภท nullable ชนิดบรรจุกล่อง - Nullable <int> ได้รับการบรรจุกล่องเพื่อการอ้างอิงแบบ null หรือ int แบบกล่อง
Jon Skeet

วิธีนั้นดีกว่า Nullable.GetUnderlyingType (type)! = null?
Kiquenet

@Kiquenet: เราไม่ได้มีประเภทที่นี่ - เพียงแค่ค่า
Jon Skeet

11

วิธีที่ง่ายที่สุดที่ฉันสามารถหาได้คือ:

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

+1 วิธีแก้ปัญหาที่ยอดเยี่ยมสำหรับกล่องแบบ null ได้ ฉันยังไม่ได้ทดสอบสิ่งนี้โดยเฉพาะ ดังนั้นหากคนอื่นสามารถยืนยันได้ก็จะได้รับการชื่นชม
TamusJRoyce

ฉันได้ทำการทดสอบแล้ว ฉันต้องสร้างNullableประเภท แต่มีความหมายที่แตกต่างกัน ในสถานการณ์ของฉันฉันควรสนับสนุนnullเป็นค่าที่ถูกต้องและไม่สนับสนุนค่าเลย ดังนั้นการสร้างOptionalประเภท เนื่องจากมีความจำเป็นต้องสนับสนุนnullค่าฉันจึงต้องใช้รหัสสำหรับการจัดการNullableค่าเป็นส่วนหนึ่งของการใช้งานของฉัน นี่คือที่มาของรหัสนี้
CARLOS LOTH

9
ฉันคิดว่าวิธีนี้ไม่ถูกต้อง ผ่านประเภทค่า Nullable เป็นข้อโต้แย้งไปยังวิธีการที่คาดว่าพารามิเตอร์ของวัตถุชนิดควรทำให้มวยเกิดขึ้น Nullable เป็นประเภทค่าและผลลัพธ์ของการแปลงมวยเป็นประเภทอ้างอิง ไม่มี nullable ชนิดบรรจุกล่อง ฉันเชื่อว่าวิธีนี้จะส่งกลับค่าเท็จเสมอ?
Mishax

1
การทดสอบเกี่ยวกับมันเหมือนคำตอบอื่น ๆ ?
Kiquenet

5
มันไม่ทำงานเพราะคุณค่าของมวย มันจะคืนค่า FALSE เสมอ
โยก

10

มีสองประเด็นที่นี่: 1) การทดสอบเพื่อดูว่าประเภทเป็นโมฆะ; และ 2) การทดสอบเพื่อดูว่าวัตถุแสดงถึงประเภท nullable

สำหรับปัญหา 1 (ทดสอบประเภท) นี่เป็นวิธีแก้ปัญหาที่ฉันใช้ในระบบของฉันเอง: TypeIsNullable-check solution

สำหรับปัญหา 2 (ทดสอบวัตถุ) โซลูชันของ Dean Chalk ด้านบนใช้งานได้กับประเภทค่า แต่มันไม่ทำงานสำหรับประเภทอ้างอิงเนื่องจากการใช้ <T> เกินพิกัดจะส่งคืนค่าเท็จเสมอ เนื่องจากประเภทการอ้างอิงนั้นเป็นโมฆะโดยเนื้อแท้การทดสอบประเภทการอ้างอิงจึงควรกลับมาจริง โปรดดูหมายเหตุ [เกี่ยวกับ "ความไร้ผล"] ด้านล่างสำหรับคำอธิบายของความหมายเหล่านี้ ดังนั้นนี่คือการแก้ไขแนวทางของ 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;
    }

และนี่คือการแก้ไขรหัสลูกค้าทดสอบของฉันสำหรับการแก้ปัญหาข้างต้น:

    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

เหตุผลที่ฉันแก้ไขแนวทางของ Dean ใน IsObjectNullable <T> (T t) คือวิธีดั้งเดิมของเขาส่งคืนเท็จเสมอสำหรับประเภทการอ้างอิง เนื่องจากวิธีการเช่น IsObjectNullable ควรสามารถจัดการค่าชนิดการอ้างอิงและเนื่องจากชนิดของการอ้างอิงทั้งหมดเป็นโมฆะโดยเนื้อแท้แล้วถ้าผ่านประเภทการอ้างอิงหรือเป็นโมฆะวิธีการควรกลับจริง

สองวิธีข้างต้นสามารถแทนที่ด้วยวิธีการเดียวต่อไปนี้และบรรลุผลลัพธ์เดียวกัน:

    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<>);
    }

อย่างไรก็ตามปัญหาของวิธีการเดียววิธีสุดท้ายนี้คือประสิทธิภาพลดลงเมื่อใช้พารามิเตอร์ Nullable <T> ใช้เวลาประมวลผลมากขึ้นในการดำเนินการบรรทัดสุดท้ายของวิธีการเดียวนี้มากกว่าการอนุญาตให้คอมไพเลอร์เลือกวิธีที่สองเกินพิกัดที่แสดงก่อนหน้านี้เมื่อใช้พารามิเตอร์ Nullable <T> -type ในการเรียก IsObjectNullable ดังนั้นทางออกที่ดีที่สุดคือการใช้วิธีสองวิธีที่แสดงไว้ที่นี่

ถ้ำ: วิธีนี้ใช้งานได้อย่างน่าเชื่อถือเฉพาะในกรณีที่ถูกเรียกโดยใช้การอ้างอิงวัตถุดั้งเดิมหรือสำเนาที่แน่นอนดังที่แสดงในตัวอย่าง อย่างไรก็ตามหากวัตถุที่ไม่มีค่าเป็นชนิดอื่น (เช่นวัตถุเป็นต้น) แทนที่จะถูกบรรจุในรูปแบบ Nullable <> ดั้งเดิมวิธีนี้จะไม่สามารถทำงานได้อย่างน่าเชื่อถือ ถ้ารหัสที่เรียกใช้วิธีนี้ไม่ได้ใช้การอ้างอิงวัตถุต้นฉบับที่ไม่มีกล่องหรือสำเนาที่แน่นอนมันไม่สามารถตรวจสอบความถูกต้องของวัตถุได้โดยใช้วิธีนี้

ในสถานการณ์การเขียนโค้ดส่วนใหญ่ในการพิจารณาความสามารถในการลบล้างเราต้องพึ่งพาการทดสอบประเภทของวัตถุดั้งเดิมไม่ใช่การอ้างอิง (เช่นรหัสจะต้องมีการเข้าถึงประเภทดั้งเดิมของวัตถุเพื่อกำหนดความถูกต้อง) ในกรณีที่พบบ่อยเหล่านี้ IsTypeNullable (ดูลิงค์) เป็นวิธีการที่เชื่อถือได้ในการพิจารณาความสามารถในการเป็นโมฆะ

PS - เกี่ยวกับ "nullability"

ฉันควรทำซ้ำคำสั่งเกี่ยวกับความถูกต้องฉันทำในโพสต์แยกซึ่งใช้โดยตรงกับที่อยู่หัวข้อนี้อย่างถูกต้อง นั่นคือฉันเชื่อว่าจุดเน้นของการอภิปรายที่นี่ไม่ควรเป็นวิธีการตรวจสอบเพื่อดูว่าวัตถุเป็นชนิด Nullable ทั่วไปหรือไม่ แต่จะว่าใครสามารถกำหนดค่า null ให้กับวัตถุประเภทนั้น ในคำอื่น ๆ ฉันคิดว่าเราควรจะกำหนดว่าประเภทวัตถุเป็นโมฆะไม่ว่าจะเป็นโมฆะ ความแตกต่างในความหมายคือเหตุผลที่เป็นประโยชน์ในการกำหนดความไร้ซึ่งมักเป็นเรื่องที่

ในระบบที่ใช้ออบเจ็กต์ที่มีชนิดที่ไม่รู้จักจนกระทั่งถึงเวลาใช้งาน (บริการบนเว็บการโทรทางไกลฐานข้อมูลฟีดและอื่น ๆ ) ข้อกำหนดทั่วไปคือการพิจารณาว่า null สามารถกำหนดให้กับวัตถุหรืออาจมีวัตถุ เป็นโมฆะ การดำเนินการดังกล่าวกับประเภทที่ไม่เป็นโมฆะอาจทำให้เกิดข้อผิดพลาดซึ่งมักจะมีข้อยกเว้นซึ่งมีราคาแพงมากทั้งในแง่ของประสิทธิภาพและความต้องการการเข้ารหัส ในการใช้แนวทางที่เป็นที่ต้องการอย่างสูงในการหลีกเลี่ยงปัญหาดังกล่าวจำเป็นต้องพิจารณาว่าวัตถุประเภทใด ๆ ที่มีความสามารถในการบรรจุโมฆะหรือไม่ นั่นคือไม่ว่าจะเป็น 'nullable' โดยทั่วไป

ในทางปฏิบัติและโดยทั่วไปแล้วความสามารถในการลบล้างใน. NET นั้นไม่ได้หมายความว่าประเภทของวัตถุนั้นเป็นรูปแบบของความว่างเปล่า ในความเป็นจริงหลาย ๆ กรณีวัตถุมีประเภทอ้างอิงสามารถมีค่าเป็นโมฆะและทำให้เป็นโมฆะทั้งหมด; สิ่งเหล่านี้ไม่มีประเภทที่เป็นโมฆะ ดังนั้นเพื่อจุดประสงค์ในทางปฏิบัติในสถานการณ์ส่วนใหญ่การทดสอบควรทำสำหรับแนวคิดทั่วไปเกี่ยวกับความไร้ประสิทธิภาพและแนวคิดที่ขึ้นกับการนำไปปฏิบัติของ Nullable ดังนั้นเราไม่ควรวางสายโดยเน้นที่. NET Nullable แต่เพียงอย่างเดียว แต่รวมความเข้าใจของเราเกี่ยวกับข้อกำหนดและพฤติกรรมในกระบวนการของการมุ่งเน้นที่แนวคิดทั่วไปที่ใช้งานได้จริง


8

โซลูชันที่ง่ายที่สุดที่ฉันใช้คือการใช้โซลูชันของ Microsoft ( วิธีการ: ระบุประเภท Nullable Type (คู่มือการเขียนโปรแกรม C #) ) เป็นวิธีการขยาย:

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

สิ่งนี้สามารถถูกเรียกเช่นนั้น:

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

นี่ก็ดูเหมือนจะเป็นวิธีที่มีเหตุผลในการเข้าถึงIsNullable()เพราะมันสอดคล้องกับIsXxxx()วิธีการอื่นทั้งหมดของTypeชั้นเรียน


1
คุณไม่ต้องการใช้ "==" แทนที่จะเป็น "! ="?
vkelman

Good spot @vkelman แทนที่จะทำการเปลี่ยนแปลงนั้นฉันได้อัปเดตคำตอบเพื่อใช้คำแนะนำปัจจุบันจาก Microsoft เนื่องจากมีการเปลี่ยนแปลงตั้งแต่ฉันเขียนสิ่งนี้
sclarke81

6

ระวังตัวด้วยการชกมวยประเภทที่ไม่สามารถใช้การได้ ( Nullable<int>หรือ int เป็นต้น):

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

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

มันเป็นประเภทการอ้างอิงที่แท้จริงดังนั้นคุณสูญเสียความจริงที่ว่ามันเป็นโมฆะ


3

อาจเป็นหัวข้อเล็กน้อย แต่ก็ยังมีข้อมูลที่น่าสนใจ ฉันพบผู้คนจำนวนมากที่ใช้Nullable.GetUnderlyingType() != nullระบุตัวตนหากประเภทเป็นโมฆะ เห็นได้ชัดว่าใช้งานได้ แต่ Microsoft แนะนำต่อไปนี้type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)(ดูhttp://msdn.microsoft.com/en-us/library/ms366789.aspx )

ฉันมองสิ่งนี้จากมุมมองด้านการแสดง บทสรุปของการทดสอบ (หนึ่งล้านครั้ง) ด้านล่างคือเมื่อประเภทเป็นโมฆะตัวเลือก Microsoft จะให้ประสิทธิภาพที่ดีที่สุด

Nullable.GetUnderlyingType (): 1335ms (ช้าลง 3 ครั้ง)

GetGenericTypeDefinition () == typeof (Nullable <>): 500ms

ฉันรู้ว่าเรากำลังพูดถึงเวลาเล็กน้อย แต่ทุกคนชอบที่จะปรับแต่งมิลลิวินาที :-)! ดังนั้นถ้าคุณเป็นเจ้านายต้องการให้คุณลดมิลลิวินาทีนี่เป็นผู้ช่วยคุณ ...

/// <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
สวัสดีอาจมีปัญหาหนึ่งเรื่องเวลาในการวัด Assert อาจส่งผลต่อผลลัพธ์ คุณผ่านการทดสอบโดยไม่ยืนยัน นอกจากนี้ Console.WriteLine ควรอยู่นอกพื้นที่มิเตอร์ +1 สำหรับความพยายามที่จะบอกปริมาณปัญหาประสิทธิภาพ :)
ipavlu

@ipavlu Console.WriteLineอยู่นอกพื้นที่มิเตอร์จริง ๆ )
nawfal

Roel ดังที่ ipavlu ได้กล่าวไว้Assertควรอยู่นอกลูป ประการที่สองคุณควรทดสอบกับกรณีที่ไม่มีค่าว่างเช่นกันเพื่อทดสอบกรณีที่ผิดพลาด ผมทดสอบที่คล้ายกัน (2 nulables และ 4 nullables ไม่ใช่) และฉันได้รับ ~ 2 วินาทีGetUnderlyingTypeและ ~ 1 วินาทีGetGenericTypeDefinitionเช่นGetGenericTypeDefinitionเป็นสองเท่าได้เร็วขึ้น (ไม่ได้สามครั้ง)
nawfal

ทำอีกรอบด้วย 2 nullables และ 2 ไม่ใช่ nullables - เวลานี้GetUnderlyingTypeคือ 2.5 เท่าช้ากว่า ด้วยค่าที่ไม่ใช่ค่า null เท่านั้น - เวลานี้ทั้งคู่เป็นคอและคอ
nawfal

แต่ที่สำคัญกว่านั้นGetUnderlyingTypeคือมีประโยชน์เมื่อคุณต้องตรวจสอบความสามารถในการทำให้เป็นโมฆะและรับประเภทพื้นฐานหากเป็นโมฆะ Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type)นี้จะเป็นประโยชน์มากและคุณมักจะเห็นรูปแบบเช่น มันก็เหมือนasคำสำคัญตรวจสอบการโยนเช่นเดียวกับมันและผลตอบแทน หากคุณต้องการรับชนิดพื้นฐานของ nullable กลับมาแล้วทำการGetGenericTypeDefinitionตรวจสอบแล้วรับประเภททั่วไปจะเป็นความคิดที่ไม่ดี นอกจากนี้ยังGetUnderlyingTypeสามารถอ่านและจดจำได้อีกมากมาย ฉันจะไม่รังเกียจถ้าฉันทำเพียง 1,000 ครั้งเท่านั้น
nawfal

0

รุ่นนี้:

  • ผลการแคชเร็วขึ้น
  • ไม่ต้องการตัวแปรที่ไม่จำเป็นเช่น Method (T obj)
  • ไม่ซับซ้อน :)
  • แค่คลาสทั่วไปแบบสแตติกที่มีฟิลด์ที่คำนวณได้ครั้งเดียว

:

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;

ฉันคิดว่าคุณตอบตัวเองด้วยการประกาศคงที่ 'is_nullable' เคล็ดลับ: ประกาศวัตถุด้วย int หรือไม่ (object a = (int?) 8;) และดูว่าเกิดอะไรขึ้น
SoLaR

0

นี่คือสิ่งที่ฉันเกิดขึ้นเพราะทุกอย่างดูเหมือนจะล้มเหลว - อย่างน้อยในPLC - Portable Class Library / .NET Coreด้วย> = C # 6

การแก้ไข:ขยายวิธีการแบบคงที่สำหรับประเภทใดTและNullable<T>และใช้ความจริงที่ว่าวิธีขยายคงที่ตรงกับประเภทต้นแบบจะถูกเรียกและจะเหนือกว่าทั่วไปTส่วนขยายวิธีการ

สำหรับT:

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

และสำหรับ Nullable<T>

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

การใช้ Reflection และtype.IsGenericType... ไม่ทำงานในชุด. NET Runtimes ปัจจุบันของฉัน และเอกสาร MSDN ก็ช่วยได้เช่นกัน

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

ส่วนหนึ่งเป็นเพราะ Reflection API มีการเปลี่ยนแปลงค่อนข้างมากใน. NET Core


0

ฉันคิดว่าสิ่งที่ใช้การทดสอบที่ Microsoft แนะนำIsGenericTypeนั้นดี แต่ในรหัสสำหรับGetUnderlyingTypeMicrosoft ใช้การทดสอบเพิ่มเติมเพื่อให้แน่ใจว่าคุณไม่ได้ผ่านการกำหนดประเภททั่วไปNullable<>:

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

-1

วิธีง่ายๆในการทำสิ่งนี้:

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

        return true;
    }

นี่คือการทดสอบหน่วยของฉันและผ่านไปทั้งหมด

    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

การทดสอบหน่วยที่แท้จริง

    [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);
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.