ฉันจะคืน NULL จากวิธีการทั่วไปใน C # ได้อย่างไร


546

ฉันมีวิธีการทั่วไปที่มีรหัส (จำลอง) (ใช่ฉันทราบว่า IList มีภาคแสดง แต่รหัสของฉันไม่ได้ใช้ IList แต่มีการรวบรวมอื่น ๆ แต่นี่ก็ไม่เกี่ยวข้องกับคำถาม ... )

static T FindThing<T>(IList collection, int id) where T : IThing, new()
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;  // ERROR: Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead.
}

สิ่งนี้ทำให้ฉันมีข้อผิดพลาดในการสร้าง

"ไม่สามารถแปลงค่าว่างเป็นพารามิเตอร์ประเภท 'T' ได้เนื่องจากอาจเป็นประเภทค่าลองใช้ 'ค่าเริ่มต้น (T)' แทน

ฉันสามารถหลีกเลี่ยงข้อผิดพลาดนี้ได้หรือไม่?


ประเภทการอ้างอิงที่เป็นโมฆะ (ใน C # 8) จะเป็นทางออกที่ดีกว่าในตอนนี้หรือไม่? docs.microsoft.com/en-us/dotnet/csharp/nullable-referencesกลับมาnullไม่ว่าจะTเป็นObjectหรือหรือint char
Alexander - Reinstate Monica

คำตอบ:


968

สองตัวเลือก:

  • ส่งคืนdefault(T)ซึ่งหมายความว่าคุณจะกลับมาnullหาก T เป็นประเภทอ้างอิง (หรือประเภทค่าที่สามารถ nullable), 0สำหรับint, '\0'สำหรับchar, เป็นต้น ( ตารางค่าเริ่มต้น (การอ้างอิง C #) )
  • จำกัด T ให้เป็นประเภทการอ้างอิงที่มีwhere T : classข้อ จำกัด แล้วกลับมาnullเป็นปกติ

3
เกิดอะไรขึ้นถ้าประเภทการคืนของฉันคือ enum ไม่ใช่คลาส? ฉันไม่สามารถระบุ T: enum :(
Justin

1
ใน. NET an enum เป็น wrapper (และค่อนข้างรั่วไหล) ที่บางมากรอบชนิดจำนวนเต็ม การประชุมคือการใช้ศูนย์สำหรับค่า enum "เริ่มต้น" ของคุณ
Mike Chamberlain

27
ฉันคิดว่าปัญหาเกี่ยวกับสิ่งนี้คือถ้าคุณใช้วิธีการทั่วไปนี้ในการพูดให้แปลงวัตถุฐานข้อมูลจาก DbNull เป็น Int และจะคืนค่าเริ่มต้น (T) โดยที่ T เป็น int ก็จะส่งกลับ 0 หากตัวเลขนี้คือ มีความหมายจริง ๆ แล้วคุณจะผ่านข้อมูลที่ไม่ดีในกรณีที่เขตข้อมูลนั้นเป็นโมฆะ หรือตัวอย่างที่ดีกว่าน่าจะเป็น DateTime หากฟิลด์นั้นเป็น "DateClosed" และมันกลับมาเป็นโมฆะเพราะและบัญชียังคงเปิดอยู่จริง ๆ แล้วมันจะเริ่มต้น (DateTime) เป็น 1/1/0000 ซึ่งหมายความว่าบัญชีนั้นถูกปิดก่อนที่จะมีการประดิษฐ์คอมพิวเตอร์
Sinaesthetic

21
@Sinaesthetic: ดังนั้นคุณจะแปลงเป็นNullable<int>หรือNullable<DateTime>แทน หากคุณใช้ประเภทที่ไม่เป็นโมฆะและจำเป็นต้องแทนค่า Null คุณก็แค่ถามหาปัญหา
Jon Skeet

1
ฉันเห็นด้วยฉันแค่อยากจะนำมันมา ฉันคิดว่าสิ่งที่ฉันทำคือเหมือน MyMethod <T> (); เพื่อถือว่าเป็นประเภทที่ไม่สามารถ nullable และ MyMethod <T?> (); สมมติว่ามันเป็นประเภทที่ไม่มีค่า ดังนั้นในสถานการณ์ของฉันฉันสามารถใช้ตัวแปรชั่วคราวเพื่อจับโมฆะและไปจากที่นั่น
Sinaesthetic

84
return default(T);

ลิงค์นี้: msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspx ควรอธิบายสาเหตุ
Harper Shelby

1
ประณามฉันจะได้ประหยัดเวลาได้มากที่ฉันรู้เกี่ยวกับคำหลักนี้ - ขอบคุณ Ricardo!
Ana Betts

1
ฉันประหลาดใจที่นี่ไม่ได้รับการโหวตมากขึ้นเนื่องจากคำหลัก 'เริ่มต้น' เป็นโซลูชันที่ครอบคลุมมากขึ้นทำให้สามารถใช้ประเภทที่ไม่อ้างอิงร่วมกับประเภทตัวเลขและโครงสร้างได้ แม้ว่าคำตอบที่ได้รับการยอมรับจะช่วยแก้ปัญหาได้ (และเป็นประโยชน์จริง ๆ ) แต่ก็ตอบได้ดีกว่าว่าจะ จำกัด ประเภทการส่งคืนเป็นประเภท nullable / อ้างอิง
Steve Jackson

33

คุณสามารถปรับเปลี่ยนข้อ จำกัด ของคุณ:

where T : class

จากนั้นอนุญาตให้ส่งคืน null ได้


ขอบคุณ ฉันไม่สามารถเลือกคำตอบ 2 ข้อเป็นโซลูชันที่ยอมรับดังนั้นฉันเลือก John Skeet เพราะคำตอบของเขามีสองวิธี
edosoft

@Migol มันขึ้นอยู่กับความต้องการของคุณ IDisposableบางทีโครงการของพวกเขาไม่จำเป็นต้องเป็น ใช่เวลาส่วนใหญ่ไม่จำเป็นต้องเป็น System.Stringไม่ได้ใช้งานIDisposableเช่น ผู้ตอบควรชี้แจงอย่างชัดเจน แต่นั่นไม่ได้ทำให้คำตอบผิด :)
ahwm

@Migol ฉันไม่รู้ว่าทำไมฉันถึงมี IDisposable อยู่ในนั้น ลบออก
TheSoftwareJedi

13

เพิ่มข้อ จำกัด คลาสเป็นข้อ จำกัด แรกในประเภททั่วไปของคุณ

static T FindThing<T>(IList collection, int id) where T : class, IThing, new()

ขอบคุณ ฉันไม่สามารถเลือกคำตอบ 2 ข้อเป็นโซลูชันที่ยอมรับดังนั้นฉันเลือก John Skeet เพราะคำตอบของเขามีสองวิธี
edosoft

7
  1. หากคุณมีวัตถุแล้วต้องพิมพ์ตัวอักษร

    return (T)(object)(employee);
  2. ถ้าคุณต้องการส่งคืน null

    return default(T);

สวัสดี user725388 โปรดยืนยันตัวเลือกแรก
Jogi Joseph George

7

ด้านล่างเป็นตัวเลือกสองตัวที่คุณสามารถใช้ได้

return default(T);

หรือ

where T : class, IThing
 return null;

6

ตัวเลือกอื่น ๆ ของคุณคือการเพิ่มสิ่งนี้ไปยังท้ายประกาศ:

    where T : class
    where T: IList

ด้วยวิธีนี้จะช่วยให้คุณคืนค่าว่าง


หากทั้งสองข้อ จำกัด where T : class, IListเป็นชนิดเดียวกันที่คุณกล่าวถึงประเภทครั้งเดียวและใช้เครื่องหมายจุลภาคเช่น หากคุณมีข้อ จำกัด ที่แตกต่างกัน, คุณทำซ้ำโทเค็นในขณะที่where where TFoo : class where TBar : IList
Jeppe Stig Nielsen

3

โซลูชันของ TheSoftwareJedi ทำงานได้

นอกจากนี้คุณยังสามารถเก็บถาวรด้วยการใช้สองค่าและประเภท nullable:

static T? FindThing<T>(IList collection, int id) where T : struct, IThing
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;
}

1

รับข้อเสนอแนะของข้อผิดพลาด ... และผู้ใช้อย่างใดอย่างหนึ่งหรือdefault(T)new T

คุณจะต้องเพิ่มการเปรียบเทียบในรหัสของคุณเพื่อให้แน่ใจว่าเป็นการจับคู่ที่ถูกต้องหากคุณไปเส้นทางนั้น

มิฉะนั้นอาจพิจารณาพารามิเตอร์เอาต์พุตสำหรับ "การจับคู่ที่พบ"


1

นี่คือตัวอย่างการใช้งานสำหรับ Nullable Enum ส่งคืนค่า:

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}

ตั้งแต่ C # 7.3 (พฤษภาคม 2018) คุณสามารถปรับปรุงข้อ จำกัดwhere TEnum : struct, Enumได้ สิ่งนี้ทำให้มั่นใจได้ว่าผู้โทรไม่ได้ระบุประเภทของค่าที่ไม่ใช่ enum โดยไม่ตั้งใจ (เช่นintหรือDateTime)
Jeppe Stig Nielsen

0

ทางเลือกอีก 2 คำตอบที่นำเสนอข้างต้น หากคุณเปลี่ยนประเภทผลตอบแทนของคุณเป็นobjectคุณสามารถกลับมาnullได้ในขณะเดียวกันก็ส่งคืนค่าที่ไม่เป็นค่าว่าง

static object FindThing<T>(IList collection, int id)
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return (T) thing;
    }
    return null;  // allowed now
}

ข้อเสีย: สิ่งนี้จะต้องมีผู้เรียกใช้วิธีการที่จะโยนวัตถุที่ส่งคืน (ในกรณีที่ไม่เป็นโมฆะ) ซึ่งหมายถึงมวย -> ประสิทธิภาพลดลง ฉันถูกไหม?
Csharpest

0

เพื่อความสมบูรณ์เรายินดีที่ได้ทราบว่าคุณสามารถทำได้เช่นกัน:

return default;

มันกลับมาเหมือนกัน return default(T);


0

สำหรับฉันมันใช้งานได้ตามที่เป็นอยู่ ปัญหาอยู่ที่ไหน

public static T FindThing<T>(this IList collection, int id) where T : IThing, new()
{
    foreach (T thing in collection)
    {
        if (thing.Id == id)
            return thing;
        }
    }

    return null; //work
    return (T)null; //work
    return null as T; //work
    return default(T); //work


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