ฉันจะตรวจสอบว่าประเภทเป็นประเภทย่อยหรือชนิดของวัตถุได้อย่างไร


335

ในการตรวจสอบว่าประเภทเป็นประเภทย่อยเป็นประเภทอื่นใน C # หรือไม่เป็นเรื่องง่าย:

typeof (SubClass).IsSubclassOf(typeof (BaseClass)); // returns true

อย่างไรก็ตามสิ่งนี้จะล้มเหลว:

typeof (BaseClass).IsSubclassOf(typeof (BaseClass)); // returns false

มีวิธีใดที่จะตรวจสอบว่าประเภทเป็นคลาสย่อยหรือคลาสพื้นฐานเองโดยไม่ต้องใช้ORโอเปอเรเตอร์หรือใช้วิธีการขยายหรือไม่

คำตอบ:


499

เห็นได้ชัดว่าไม่มี

นี่คือตัวเลือก:

Type.IsSubclassOf

ตามที่คุณค้นพบแล้วสิ่งนี้จะไม่ทำงานหากทั้งสองประเภทเหมือนกันนี่คือตัวอย่างโปรแกรมLINQPadที่สาธิต:

void Main()
{
    typeof(Derived).IsSubclassOf(typeof(Base)).Dump();
    typeof(Base).IsSubclassOf(typeof(Base)).Dump();
}

public class Base { }
public class Derived : Base { }

เอาท์พุท:

True
False

ซึ่งบ่งชี้ว่าDerivedเป็นคลาสย่อยของBaseแต่นั่นBaseคือ (แน่นอน) ไม่ใช่คลาสย่อยของตัวเอง

Type.IsAssignableFrom

ตอนนี้จะตอบคำถามของคุณโดยเฉพาะ แต่จะให้ผลบวกที่ผิด ตามที่ Eric Lippert ได้ชี้ให้เห็นในความคิดเห็นในขณะที่วิธีการจะตอบกลับTrueสำหรับคำถามทั้งสองข้างต้นอย่างแน่นอนแต่ก็จะส่งคืนTrueสิ่งเหล่านี้ซึ่งคุณอาจไม่ต้องการ:

void Main()
{
    typeof(Base).IsAssignableFrom(typeof(Derived)).Dump();
    typeof(Base).IsAssignableFrom(typeof(Base)).Dump();
    typeof(int[]).IsAssignableFrom(typeof(uint[])).Dump();
}

public class Base { }
public class Derived : Base { }

ที่นี่คุณจะได้รับผลลัพธ์ต่อไปนี้:

True
True
True

สุดท้ายTrueก็จะบ่งบอกถ้าวิธีการเพียงตอบคำถามที่ถามว่าuint[]สืบทอดมาจากint[]หรือว่าพวกเขากำลังชนิดเดียวกันที่ชัดเจนไม่ได้เป็นกรณี

ดังนั้นIsAssignableFromไม่ถูกต้องทั้งหมดเช่นกัน

is และ as

"ปัญหา" ที่มีisและasในบริบทของคำถามของคุณคือพวกเขาจะต้องการให้คุณดำเนินการกับวัตถุและเขียนหนึ่งในประเภทโดยตรงในรหัสและไม่ทำงานกับTypeวัตถุ

กล่าวอีกนัยหนึ่งสิ่งนี้จะไม่รวบรวม:

SubClass is BaseClass
^--+---^
   |
   +-- need object reference here

และสิ่งนี้จะไม่:

typeof(SubClass) is typeof(BaseClass)
                    ^-------+-------^
                            |
                            +-- need type name here, not Type object

และสิ่งนี้จะไม่:

typeof(SubClass) is BaseClass
^------+-------^
       |
       +-- this returns a Type object, And "System.Type" does not
           inherit from BaseClass

ข้อสรุป

ในขณะที่วิธีการข้างต้นอาจตรงกับความต้องการของคุณคำตอบที่ถูกต้องสำหรับคำถามของคุณ (ตามที่ฉันเห็น) คือคุณจะต้องตรวจสอบเพิ่มเติม:

typeof(Derived).IsSubclassOf(typeof(Base)) || typeof(Derived) == typeof(Base);

ซึ่งแน่นอนมากขึ้นในวิธีการ:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}

2
ขอบคุณ! ฉันจะทำเครื่องหมายสิ่งนี้เป็นคำตอบที่ถูกต้อง (ต้องรออีก 8 นาที) เนื่องจากคุณพูดถึงว่าจะต้องมีการกลับรายการเช็คและให้ลิงก์ไปยังเอกสาร MSDN
Daniel T.

71
โปรดทราบว่าสิ่งนี้ไม่ได้ทำในสิ่งที่คำถามต้องการ สิ่งนี้ไม่ได้ตัดสินว่าประเภทหนึ่งเป็นประเภทย่อยของประเภทอื่นหรือไม่ แต่ประเภทหนึ่งนั้นได้รับการมอบหมายให้เข้ากันได้กับประเภทอื่น อาร์เรย์ของ uint ไม่ใช่คลาสย่อยของอาร์เรย์ int แต่สามารถใช้ร่วมกับการมอบหมายได้ IEnumerable <Giraffe> ไม่ใช่ subclass ของ IEnumerable <Animal> แต่มันสามารถทำงานร่วมกันได้ใน v4
Eric Lippert

ไม่มีวิธีการทำสิ่งนี้ในชื่อวิธีการเช่น Java? `` `เป็นโมฆะ <? ขยาย Base> saveObject (? objectToSave) `` `
Oliver Dixon

2
จะIsInstanceOfTypeพอดีกับสิ่งนี้ได้อย่างไร
Lennart

มันอาจจะดึงดูดการแปลง IsSameOrSubclass เป็นวิธีการขยาย แต่ฉันแนะนำต่อมันมันเป็นบิตที่น่าอึดอัดใจในการอ่านและเขียนและง่ายต่อการเลอะเลือน
jrh



1

หากคุณกำลังพยายามที่จะทำในโครงการ Xamarin Forms PCL การแก้ปัญหาข้างต้นโดยใช้IsAssignableFromจะให้ข้อผิดพลาด:

ข้อผิดพลาด: 'Type' ไม่มีคำจำกัดความสำหรับ 'IsAssignableFrom' และไม่มีวิธีการขยาย 'IsAssignableFrom' ที่ยอมรับอาร์กิวเมนต์แรกของชนิด 'Type' (คุณกำลังใช้คำสั่งหรือการอ้างอิงชุดประกอบ)

เพราะIsAssignableFromถามหาTypeInfoวัตถุ คุณสามารถใช้GetTypeInfo()วิธีการจากSystem.Reflection:

typeof(BaseClass).GetTypeInfo().IsAssignableFrom(typeof(unknownType).GetTypeInfo())


0

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

public class A
{
}

public class B : A
{
}

public class MyClass
{
    private Type _helperType;
    public Type HelperType
    {
        get { return _helperType; }
        set 
        {
            var testInstance = (A)Activator.CreateInstance(value);
            if (testInstance==null)
                throw new InvalidCastException("HelperType must be derived from A");
            _helperType = value;
        }
    }
}

ฉันรู้สึกว่าฉันอาจจะไร้เดียงสาเล็กน้อยที่นี่เพื่อรับข้อเสนอแนะใด ๆ


2
มีปัญหาสองสามข้อที่มีแนวคิดดังกล่าว: 1) ประเภทที่ต้องการคอนสตรัคเตอร์แบบไม่มีพารามิเตอร์มิฉะนั้น CreateInstance จะล้มเหลว 2) การร่ายไปยัง (A) ไม่ส่งคืนค่าว่างหากการโยนไม่สามารถทำได้ 3) คุณไม่ต้องการอินสแตนซ์ใหม่ดังนั้นคุณจึงมีการจัดสรรที่ไร้ประโยชน์ คำตอบที่ยอมรับได้ดีกว่า (แม้ว่าจะไม่สมบูรณ์แบบ)
Marcel Popescu

ขอบคุณสำหรับความคิดเห็น. มีประโยชน์มาก
baskren

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