C #에서 객체가 일반 유형인지 테스트


134

객체가 제네릭 형식인지 테스트하고 싶습니다. 나는 성공하지 않고 다음을 시도했다.

public bool Test()
{
    List<int> list = new List<int>();
    return list.GetType() == typeof(List<>);
}

내가 뭘 잘못하고 있으며 어떻게이 테스트를 수행합니까?

답변:


201

제네릭 형식의 인스턴스인지 확인하려면 다음을 수행하십시오.

return list.GetType().IsGenericType;

그것이 일반인지 확인하려면 List<T>:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>);

Jon이 지적했듯이 정확한 유형 동등성을 확인합니다. 리턴 false은 반드시 list is List<T>리턴을 의미하지는 않습니다 false(즉, 오브젝트를 List<T>변수에 지정할 수 없음 ).


9
하위 유형은 감지하지 못합니다. 내 대답을 참조하십시오. 또한 인터페이스가 훨씬 어렵습니다. (
Jon Skeet

1
제네릭 형식이 아닌 경우 GetGenericTypeDefinition에 대한 호출이 발생합니다. 먼저 확인하십시오.
Kilhoffer

85

유형이 제네릭인지 여부를 알고 싶지는 않지만 객체가 유형 인수를 모른 채 특정 제네릭 유형의 인스턴스인지 알고 싶다고 가정합니다.

불행히도 매우 간단하지 않습니다. 제네릭 형식이 클래스 인 경우 (나쁜 경우) 그렇게 나쁘지는 않지만 인터페이스가 더 어렵습니다. 클래스 코드는 다음과 같습니다.

using System;
using System.Collections.Generic;
using System.Reflection;

class Test
{
    static bool IsInstanceOfGenericType(Type genericType, object instance)
    {
        Type type = instance.GetType();
        while (type != null)
        {
            if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == genericType)
            {
                return true;
            }
            type = type.BaseType;
        }
        return false;
    }

    static void Main(string[] args)
    {
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new List<string>()));
        // False
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new string[0]));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList()));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList<int>()));
    }

    class SubList : List<string>
    {
    }

    class SubList<T> : List<T>
    {
    }
}

편집 : 의견에서 언급했듯이 인터페이스에서 작동 할 수 있습니다.

foreach (var i in type.GetInterfaces())
{
    if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
}

나는 이것 주위에 어색한 경우가있을 수 있지만 몰래 의심이 있지만 지금은 실패하는 것을 찾을 수 없습니다.


2
이 문제를 방금 발견했습니다. 그것은 한 줄의 상속만을 겪습니다. 도중에 기본 클래스 원하는 인터페이스 가 모두있는 기본 이 있으면 클래스 경로 만 내려갑니다.
Groxx

1
@Groxx : 맞습니다. 방금 대답은 다음과 같습니다. "제네릭 형식이 클래스 인 경우 (이 경우처럼) 너무 나쁘지는 않지만 인터페이스가 더 어려워요. 여기에 클래스 코드가 있습니다."
Jon 스키트

1
<T>를 알 수있는 방법이 없으면 어떻게합니까? 마찬가지로 int 또는 string 일 수도 있지만 알 수는 없습니다. 이것은 거짓 긍정처럼 보일 것입니다 ... 따라서 사용할 T가 없으며 일부 객체의 속성을 살펴보고 있으며 하나는 목록입니다. 당신이 그것을 떼어 놓을 수있는 목록인지 어떻게 알 수 있습니까? 이것은 당신이 어디에서나 사용할 수있는 T가 없다는 것을 의미합니다. 모든 유형을 추측 할 수는 있지만 (List <int>? List <string>?) 알고 싶은 것은 IS AA LIST입니까? 그 질문에 대답하기 어려운 것 같습니다.

@RiverC : 네, 그렇습니다 . 여러 가지 이유로 대답 하기 상당히 어렵습니다. 클래스에 대해서만 이야기한다면, 그렇게 나쁘지 않습니다 ... 상속 트리를 계속 걷고 List<T>어떤 형태로 충돌했는지 확인할 수 있습니다 . 인터페이스를 포함하면 정말 까다 롭습니다.
Jon Skeet

3
당신의 루프를 대체 할 수 IsInstanceOfGenericType에 전화로 IsAssignableFrom(대신 항등 연산자 ==)?
slawekwin

7

동적 해법을 사용하여 더 짧은 코드를 사용할 수 있습니다. 순수한 반사보다 느릴 수 있습니다.

public static class Extension
{
    public static bool IsGenericList(this object o)
    {
       return IsGeneric((dynamic)o);
    }

    public static bool IsGeneric<T>(List<T> o)
    {
       return true;
    }

    public static bool IsGeneric( object o)
    {
        return false;
    }
}



var l = new List<int>();
l.IsGenericList().Should().BeTrue();

var o = new object();
o.IsGenericList().Should().BeFalse();

7

다음은 제네릭 형식 검사의 가장 일반적인 경우를 다루는 가장 좋아하는 두 가지 확장 방법입니다.

와 일하다:

  • 다중 (일반) 인터페이스
  • 여러 (일반) 기본 클래스
  • true를 반환하면 특정 제네릭 형식을 '아웃'하는 과부하가 있습니다 (샘플에 대한 단위 테스트 참조).

    public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
    {
        Type concreteType;
        return typeToCheck.IsOfGenericType(genericType, out concreteType); 
    }
    
    public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
    {
        while (true)
        {
            concreteGenericType = null;
    
            if (genericType == null)
                throw new ArgumentNullException(nameof(genericType));
    
            if (!genericType.IsGenericTypeDefinition)
                throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
    
            if (typeToCheck == null || typeToCheck == typeof(object))
                return false;
    
            if (typeToCheck == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if (genericType.IsInterface)
                foreach (var i in typeToCheck.GetInterfaces())
                    if (i.IsOfGenericType(genericType, out concreteGenericType))
                        return true;
    
            typeToCheck = typeToCheck.BaseType;
        }
    }

다음은 (기본) 기능을 보여주는 테스트입니다.

 [Test]
    public void SimpleGenericInterfaces()
    {
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));

        Type concreteType;
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
        Assert.AreEqual(typeof(IEnumerable<string>), concreteType);

        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
        Assert.AreEqual(typeof(IQueryable<string>), concreteType);


    }

0
return list.GetType().IsGenericType;

3
다른 질문에 맞습니다. 이 질문의 경우 문제의 절반 만 해결하기 때문에 잘못되었습니다.
Groxx

1
Stan R의 대답은 실제로 제기 된 질문에 대답하지만 OP가 실제로 의미하는 것은 "개체가 C #에서 특정 제네릭 유형 인지 테스트하는 것 "이었습니다.이 대답은 실제로 불완전합니다.
yoyo

사람들은 "제네릭 타입"보다는 "제네릭 타입"이라는 맥락에서 질문에 답변했기 때문에 저에게 투표를합니다. 영어는 제 2 언어이며 그러한 언어의 뉘앙스는 종종 저를지나갑니다. 방어를 위해 OP는 특정 유형에 대해 테스트하도록 구체적으로 요구하지 않았으며 제목에서 "is of"일반 유형을 묻습니다 ... 모호한 질문.
Stan R.

2
이제 당신은 그것을 알고 당신은 더 구체적이고 정확한 답변을 향상시킬 수 있습니다.
피터 이반
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.