วิธีการตรวจสอบว่าชนิดที่ใช้อินเทอร์เฟซกับ C # สะท้อน


562

ไม่สะท้อนในC#ทางที่เป็นข้อเสนอเพื่อตรวจสอบว่าได้รับบางSystem.Typeรุ่นประเภทอินเตอร์เฟซบางอย่าง?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);

คำตอบ:


969

คุณมีตัวเลือกน้อย:

  1. typeof(IMyInterface).IsAssignableFrom(typeof(MyType))

  2. typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface))

สำหรับอินเตอร์เฟสทั่วไปมันแตกต่างกันเล็กน้อย

typeof(MyType).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMyInterface<>))

68
โปรดจำไว้ว่า typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) ก็เป็นจริงเช่นกันซึ่งอาจมีผลลัพธ์ที่ไม่คาดคิดในรหัสของคุณ
Chris Kemp

29
มันแน่ใจว่าเป็นเรื่องง่ายที่จะไม่ให้ความสนใจและได้รับข้อโต้แย้งสำหรับIsAssignableFromถอยหลัง ฉันจะไปด้วยGetInterfaces: p
เบนจามิน

12
IsAssignableFrom(t1)ตัวแปรที่เป็นเรื่องเกี่ยวกับ 3x เร็วกว่าGetInterfaces().Contains(t2)คู่ในรหัสของฉัน
Pierre Arnaud

24
@PierreArnaud: IsAssignableFrom จะเรียก GetInterfaces ในที่สุดดังนั้นการทดสอบของคุณอาจตรวจสอบ GetInterfaces ก่อนและ IsAssignable หลังจากนั้น นั่นเป็นเพราะ GetInterfaces เก็บผลลัพธ์ของมันไว้ดังนั้นการเรียกใช้ครั้งแรกจึงมีค่าใช้จ่ายมากขึ้น
Panos Theof

17
การเปลี่ยนแปลงเล็กน้อยในคำตอบของ @ Kosta ด้วย C # 6 เราสามารถทำได้typeof(MyType).GetInterface(nameof(IMyInterface)) != nullเพื่อความปลอดภัยที่ดีกว่าและการปรับโครงสร้างใหม่
aholmes


32
typeof(IMyInterface).IsAssignableFrom(someclass.GetType());

หรือ

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));

34
หากคุณมีอินสแตนซ์ของคลาสอยู่แล้ววิธีการที่ดีกว่านั้นง่ายมากsomeclass is IMyInterfaceเนื่องจากไม่ต้องเสียค่าใช้จ่ายในการสะท้อนเลย ดังนั้นในขณะที่ไม่ผิดก็ไม่ใช่วิธีที่เหมาะที่จะทำ
James J. Regan IV

1
@James - เห็นด้วย แม้แต่ Resharper ก็ให้คำแนะนำเดียวกัน
Angshuman Agarwal

@ JamesJ.ReganIV คุณควรโพสต์ว่าเป็นคำตอบฉันเกือบจะพลาดความคิดเห็นของคุณ
reggaeguitar

@reggaeguitar ขอบคุณ แต่ความคิดเห็นไม่ตอบคำถามเดิม คำถามถามถึงวิธีแก้ปัญหาการสะท้อนแสงฉันแค่พูดในกรณีแรกของคำตอบนี้ที่คุณมีตัวอย่างของการสะท้อนวัตถุไม่ใช่วิธีแก้ปัญหาในอุดมคติ
James J. Regan IV

1
@ JamesJ.ReganIV ที่จริงแล้วisตรวจสอบทั้งสองทิศทางของลำดับชั้นการสืบทอดในขณะที่IsAssignableFromเพียงตรวจสอบขึ้นไป นอกจากนี้หากคุณมีอินสแตนซ์ของวัตถุคุณควรโทรหาIsInstanceOfType(ซึ่งมีลักษณะเฉพาะขึ้นด้านบน)
Sellorio

13
public static bool ImplementsInterface(this Type type, Type ifaceType) 
{
    Type[] intf = type.GetInterfaces();
    for(int i = 0; i < intf.Length; i++) 
    {
        if(intf[ i ] == ifaceType) 
        {
            return true;
        }
    }
    return false;
}

ฉันคิดว่านี่เป็นรุ่นที่ถูกต้องด้วยเหตุผลสามประการ:

  1. มันใช้ GetInterfaces และไม่ใช่ IsAssignableFrom มันเร็วกว่าเนื่องจาก IsAssignableFrom ในที่สุดหลังจากการตรวจสอบหลายครั้งเรียก GetInterfaces
  2. มันวนซ้ำอาร์เรย์โลคัลดังนั้นจะไม่มีการตรวจสอบขอบเขต
  3. มันใช้ตัวดำเนินการ == ซึ่งกำหนดไว้สำหรับ Type ดังนั้นอาจปลอดภัยกว่าเมธอด Equals (ซึ่งการเรียกประกอบด้วยจะใช้ในที่สุด)

10
+1 สำหรับเนื้อหาฉันเกลียดช่องว่างรอบ ๆ parens และวงเล็บปีกกาอียิปต์ นอกจากนี้วิธีการทั้งหมดสามารถเขียนเป็น: return type.GetInterfaces () ใด ๆ (t => t == ifaceType) ใด ๆ
reggaeguitar

1
Type.IsAssignableFrom () internaly ทำหน้าที่เหมือนกับรหัสของคุณ
devi

1
ยังทำไมไม่พิมพ์ GETInterfaces (). ประกอบด้วย (ifaceType) ซึ่งไม่ได้ใช้ LINQ

9

ฉันเพิ่งทำ:

public static bool Implements<I>(this Type source) where I : class
{
  return typeof(I).IsAssignableFrom(source);
}

ฉันหวังว่าฉันจะได้พูดwhere I : interfaceแต่interfaceไม่ใช่ตัวเลือกข้อ จำกัด พารามิเตอร์ทั่วไป classใกล้เคียงที่สุดเท่าที่จะได้รับ

การใช้งาน:

if(MyType.Implements<IInitializable>())
  MyCollection.Initialize();

ฉันเพิ่งพูดImplementsเพราะมันใช้งานง่ายกว่า ฉันIsAssignableFromพลิกตัวตลอดเวลา


คุณสามารถทำได้return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);เพื่อคืนค่าเท็จในการใช้วิธีการที่ 'ไม่ถูกต้อง' นั่นคือ ใช้กับชนิดคลาสแทนชนิดของอินเทอร์เฟซหรือโยนข้อยกเว้นถ้าพารามิเตอร์ชนิดไม่ใช่อินเทอร์เฟซ แม้ว่าคุณจะสามารถยืนยันได้ว่าคลาสที่ได้รับ 'นำ' มาเป็นพ่อแม่ ...
Sindri Jóelsson

7

การแก้ไขคำตอบของ Jeff เพื่อประสิทธิภาพที่ดีที่สุด (ขอบคุณการทดสอบประสิทธิภาพโดย Pierre Arnaud):

var type = typeof(MyType);
var implementsInterface = typeof(IMyInterface).IsAssignableFrom(type) && type.IsClass;

เพื่อค้นหาทุกชนิดที่ใช้อินเตอร์เฟซในการที่ได้รับAssembly:

var implementations = typeof(TypeInTargetAssembly).Assembly.GetTypes()
                          .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && t.IsClass);

7

อย่างที่คนอื่นพูดถึงแล้ว: เบนจามิน 10 เม.ย. 56 เวลา 22:21 น. "

มันแน่ใจว่าเป็นเรื่องง่ายที่จะไม่ใส่ใจและรับอาร์กิวเมนต์สำหรับ IsAssignable จากข้างหลัง ฉันจะไปกับ GetInterfaces ทันที: p -

อีกวิธีคือการสร้างวิธีการขยายสั้น ๆ ที่ตอบสนองวิธีคิดที่ "ปกติ" (และตกลงกันว่านี่เป็นทางเลือกส่วนตัวเล็กน้อยที่จะทำให้มัน "เป็นธรรมชาติมากขึ้น" ตามความชอบ ):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }
}

และทำไมไม่ไปที่กว้างกว่านี้อีกสักหน่อย (ก็ไม่แน่ใจว่ามันน่าสนใจจริง ๆ หรือเปล่าฉันว่าฉันแค่ผ่านการซินแทรค 'น้ำตาล')

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }

    public static bool IsAssignableTo<TAssignable>(this Type type)
    {
        return IsAssignableTo(type, typeof(TAssignable));
    }
}

ฉันคิดว่ามันอาจจะดูเป็นธรรมชาติมากกว่าเดิม แต่อีกครั้งแค่เรื่องความคิดเห็นส่วนตัว:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();

4
มีเหตุผลที่คุณไม่เพียง แต่นำการติดตั้งไปใช้โดยตรงในวิธีการส่วนขยายหรือไม่ ฉันหมายถึงแน่ใจว่าสิ่งนี้ช่วยให้คุณเรียกมันได้ทั้งสองทาง แต่ทำไมคุณต้องทำเช่นนั้น?
Mark A. Donohoe

@MarqueIV เสียใจที่จะได้รับกลับมาให้คุณเกือบ 2 ปีที่ปลายดีผมคิดว่ามันเป็นนิสัยที่ไม่ดีเก่ากลับมาแล้วจะห่อวิธีการช่วยเหลือในวิธีขยายเพื่อหลีกเลี่ยงการทำซ้ำรหัสจะแก้ไขคำตอบของฉัน :)
เคอร์รี Perret

1
@ MarqueIV ทำไปแล้วและเปลี่ยนนิสัยที่ไม่ดีอื่น ๆ ของฉันที่ไม่ใช้นามแฝงนั่นคือBoolean=> bool(ฉันทำไม่ได้ว่าทำไมฉันถึงมีกฎเข้มงวด "แฟนซี" ของการเข้ารหัสเมื่อฉันยังเด็ก)
Kerry Perret

3

หากคุณมีประเภทหรืออินสแตนซ์คุณสามารถตรวจสอบได้อย่างง่ายดายว่าพวกเขาสนับสนุนอินเทอร์เฟซเฉพาะ

วิธีทดสอบว่าวัตถุใช้ส่วนต่อประสานที่แน่นอนหรือไม่:

if(myObject is IMyInterface) {
  // object myObject implements IMyInterface
}

วิธีทดสอบว่าใช้อินเทอร์เฟซที่แน่นอนได้หรือไม่:

if(typeof(IMyInterface).IsAssignableFrom(typeof(MyType))) {
  // type MyType implements IMyInterface
}

หากคุณมีอ็อบเจกต์สามัญและต้องการที่จะทำการแคสต์รวมถึงการตรวจสอบว่าอินเตอร์เฟสที่คุณแคสต์นั้นถูกนำไปใช้งานหรือไม่รหัสคือ:

 var myCastedObject = myObject as IMyInterface;

    if(myCastedObject != null) {
      // object myObject implements IMyInterface
    }


1

ทุกคนที่ค้นหาสิ่งนี้อาจพบว่าวิธีการต่อไปนี้มีประโยชน์:

public static class TypeExtensions
{
    public static bool ImplementsInterface(this Type type, Type @interface)
    {
        if (type == null)
        {
            throw new ArgumentNullException(nameof(type));
        }

        if (@interface == null)
        {
            throw new ArgumentNullException(nameof(@interface));
        }

        var interfaces = type.GetInterfaces();
        if (@interface.IsGenericTypeDefinition)
        {
            foreach (var item in interfaces)
            {
                if (item.IsConstructedGenericType && item.GetGenericTypeDefinition() == @interface)
                {
                    return true;
                }
            }
        }
        else
        {
            foreach (var item in interfaces)
            {
                if (item == @interface)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

การทดสอบ xunit:

public class TypeExtensionTests
{
    [Theory]
    [InlineData(typeof(string), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<int>), true)]
    [InlineData(typeof(List<int>), typeof(IList<string>), false)]
    public void ValidateTypeImplementsInterface(Type type, Type @interface, bool expect)
    {
        var output = type.ImplementsInterface(@interface);
        Assert.Equal(expect, output);
    }
}

0

เกี่ยวกับอะไร

if(MyType as IMyInterface != null)

?


4
เห็นได้ชัดเมื่อฉันมีอินสแตนซ์ ไม่มีประโยชน์เมื่อฉันมีประเภทจากการสะท้อน
edc65


0

คำตอบที่ถูกต้องคือ

typeof(MyType).GetInterface(nameof(IMyInterface)) != null;

อย่างไรก็ตาม

typeof(MyType).IsAssignableFrom(typeof(IMyInterface));

อาจส่งคืนผลลัพธ์ที่ผิดเนื่องจากโค้ดต่อไปนี้แสดงด้วยสตริงและ IConvertible:

    static void TestIConvertible()
    {
        string test = "test";
        Type stringType = typeof(string); // or test.GetType();

        bool isConvertibleDirect = test is IConvertible;
        bool isConvertibleTypeAssignable = stringType.IsAssignableFrom(typeof(IConvertible));
        bool isConvertibleHasInterface = stringType.GetInterface(nameof(IConvertible)) != null;

        Console.WriteLine($"isConvertibleDirect: {isConvertibleDirect}");
        Console.WriteLine($"isConvertibleTypeAssignable: {isConvertibleTypeAssignable}");
        Console.WriteLine($"isConvertibleHasInterface: {isConvertibleHasInterface}");
    }

ผล:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True

4
IsAssignableFromที่คุณสามารถดูในคำตอบที่ยอมรับคุณสบตาประเภทในการใช้งานของ เช่นเดียวกับเบนจามินและโอวานเตือน
VV5198722

0

โปรดทราบว่าหากคุณมีอินเทอร์เฟซทั่วไปIMyInterface<T>สิ่งนี้จะส่งคืนเสมอfalse:

  typeof(IMyInterface<>).IsAssignableFrom(typeof(MyType)) /* ALWAYS FALSE */

สิ่งนี้ไม่ทำงาน:

  typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface<>))  /* ALWAYS FALSE */

อย่างไรก็ตามหากMyTypeใช้IMyInterface<MyType>งานได้และส่งคืนtrue:

  typeof(IMyInterface<MyType>).IsAssignableFrom(typeof(MyType))

แต่คุณอาจจะไม่ทราบว่าพารามิเตอร์ชนิดTที่รันไทม์ ทางออกที่ค่อนข้างแฮ็คคือ:

  typeof(MyType).GetInterfaces()
                .Any(x=>x.Name == typeof(IMyInterface<>).Name)

วิธีการแก้ปัญหาของเจฟฟ์แฮ็คน้อยลงเล็กน้อย:

  typeof(MyType).GetInterfaces()
         .Any(i => i.IsGenericType 
             && i.GetGenericTypeDefinition() == typeof(IMyInterface<>));

นี่คือวิธีการขยายTypeที่เหมาะกับกรณีใด ๆ :

public static class TypeExtensions
{
    public static bool IsImplementing(this Type type, Type someInterface)
    {
        return type.GetInterfaces()
             .Any(i => i == someInterface 
                 || i.IsGenericType 
                    && i.GetGenericTypeDefinition() == someInterface);
    }
}

(โปรดทราบว่าข้างต้นใช้ linq ซึ่งอาจช้ากว่าลูป)

จากนั้นคุณสามารถทำได้:

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