제네릭 클래스 또는 메서드의 멤버에서 T 유형을 얻는 방법은 무엇입니까?


675

클래스 또는 메서드에 일반 멤버가 있다고 가정 해 보겠습니다.

public class Foo<T>
{
    public List<T> Bar { get; set; }

    public void Baz()
    {
        // get type of T
    }   
}

나는 클래스를 인스턴스화 할 때이 T되고 MyTypeObject1, 그래서 클래스는 일반적인 목록 속성이 있습니다 List<MyTypeObject1>. 일반이 아닌 클래스의 일반 메소드에도 동일하게 적용됩니다.

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

클래스 목록에 어떤 유형의 객체가 포함되어 있는지 알고 싶습니다. 따라서 호출 된 목록 속성 Bar또는 로컬 변수 baz에는 어떤 유형의 T?

Bar[0].GetType()목록에 0 요소가 포함될 수 있기 때문에 할 수 없습니다 . 어떻게하니?

답변:


695

올바르게 이해하면 목록에 컨테이너 클래스 자체와 동일한 유형 매개 변수가 있습니다. 이 경우에는 다음을 수행하십시오.

Type typeParameterType = typeof(T);

운 좋게도 objecttype 매개 변수로 사용하는 경우 Marc의 답변을 참조하십시오 .


4
Lol-그렇습니다. 나는 OP 만 있다고 가정한다 object, IList그러나 이것은 아주 잘 올바른 해답이 될 수있다 -, 또는 유사한.
Marc Gravell

32
나는 읽기 쉬운 것을 좋아합니다 typeof. 당신이, T의 종류를 알고 싶다면 바로 사용 typeof(T):
demoncodemonkey

2
실제로 방금 typeof (Type)을 사용했으며 훌륭하게 작동합니다.
Anton

1
그러나 일반적인 매개 변수에는 typeof ()를 사용할 수 없습니다.
레이니 반

2
@Reynevan 물론 typeof()일반 매개 변수와 함께 사용할 수 있습니다 . 작동하지 않는 예가 있습니까? 아니면 유형 매개 변수와 참조를 혼동합니까?
Luaan

520

(참고 : 나는 당신이 아는 모든 것이 object또는IList 또는 유사한하고 목록이 실행시에 모든 유형이 될 수 있음)

당신이 그것을 알고 있다면 List<T>:

Type type = abc.GetType().GetGenericArguments()[0];

또 다른 옵션은 인덱서를 보는 것입니다.

Type type = abc.GetType().GetProperty("Item").PropertyType;

새로운 TypeInfo 사용하기 :

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];

1
유형 유형 = abc.GetType (). GetGenericArguments () [0]; 경계의 배열 인덱스의 ==> 아웃 ...
패트릭 Desjardins에

28
@Daok : 그렇다면 그것은리스트가 아니다 <T>
Marc Gravell

BindingList 또는 List 또는 <T>를 보유한 모든 오브젝트에 필요한 것이 있습니다. 내가하고있는 일은 커스텀 BindingListView <T>를 사용하십시오.
Patrick Desjardins

1
BindingList <T>를 사용해보십시오. 우리의 BindingListView <T>는 BindingList <T>에서 상속하며 둘 다 귀하의 옵션을 모두 시도했지만 작동하지 않습니다. 나는 뭔가 잘못했을 수도 있지만 ...이 솔루션은 List <T> 유형에는 작동하지만 다른 유형의 목록에는 작동하지 않는다고 생각합니다.
Patrick Desjardins

유형 유형 = abc.GetType (). GetProperty ( "Item"). PropertyType; 대신 MyObject를 반환 BindingListView <MyObject를> ...
패트릭 Desjardins에

49

다음 확장 방법을 사용하면 리플렉션없이 벗어날 수 있습니다.

public static Type GetListType<T>(this List<T> _)
{
    return typeof(T);
}

또는 더 일반적인 :

public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return typeof(T);
}

용법:

List<string>        list    = new List<string> { "a", "b", "c" };
IEnumerable<string> strings = list;
IEnumerable<object> objects = list;

Type listType    = list.GetListType();           // string
Type stringsType = strings.GetEnumeratedType();  // string
Type objectsType = objects.GetEnumeratedType();  // BEWARE: object

10
T컴파일시 유형을 이미 알고있는 경우에만 유용합니다 . 이 경우 코드가 전혀 필요하지 않습니다.
재귀 적

'기본 반환 (T);'
fantastory

1
@recursive : 익명 유형 목록으로 작업하는 경우 유용합니다.
JJJ

나는 당신의 대답을 읽기 전에 똑같은 일을했지만 나는 내 전화했다ItemType
toddmo

31

시험

list.GetType().GetGenericArguments()

7
new List <int> (). GetType (). GetGenericArguments ()는 System.Int32를 항목으로하여 System.Type [1]을 여기에 반환합니다.
Rauhotz

@Rauhotz이 GetGenericArguments메소드는의 Array 객체를 반환합니다.이 객체는 Type필요한 Generic Type의 위치를 ​​파싱해야합니다. 같은 Type<TKey, TValue>: 당신이해야 GetGenericArguments()[0]얻을 TKey유형을하고 GetGenericArguments()[1]얻을 TValue유형
GoldBishop

14

그것은 나를 위해 일한 것입니다. 여기서 myList는 알 수없는 종류의 목록입니다.

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;

1
나는 그것이 형식 인수가 필요하다는 오류가 발생 (예 <T>)
조셉 Humfrey

Joseph과 다른 사람들은 오류를 제거하기 위해 System.Collections에 있습니다.
user2334883

1
두 번째 줄만 있으면됩니다. A List는 이미의 구현 IEnumerable이므로 캐스트는 아무것도 추가하지 않는 것 같습니다. 그러나 감사합니다. 좋은 해결책입니다.
pipedreambomb

10

전체 Type 변수가 필요하지 않고 유형을 확인하려는 경우 임시 변수를 쉽게 만들 수 있으며 연산자는 연산자입니다.

T checkType = default(T);

if (checkType is MyClass)
{}

9

이 유형을 일반 유형의 리턴 유형으로 사용할 수 있습니다.

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

8

이것을 고려하십시오 : 나는 그것을 같은 방식으로 20 유형 목록을 내보내는 데 사용합니다 :

private void Generate<T>()
{
    T item = (T)Activator.CreateInstance(typeof(T));

    ((T)item as DemomigrItemList).Initialize();

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
    if (type == null) return;
    if (type != typeof(account)) //account is listitem in List<account>
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}

1
T가 실제 추가 된 객체의 추상 수퍼 클래스 인 경우에는 작동하지 않습니다. 말할 것도없고, new T();와 같은 일을 (T)Activator.CreateInstance(typeof(T));합니다. where T : new()클래스 / 함수 정의에 추가 해야하지만 객체를 만들 려면 어쨌든 수행해야합니다.
Nyerguds

또한, 호출 GetTypeA의 FirstOrDefault잠재적 널 참조 예외의 결과 항목. 적어도 하나의 항목을 반환한다고 확신한다면 First대신 사용 하지 않겠습니까?
Mathias Lykkegaard Lorenzen

6

나는이 확장 방법을 사용하여 비슷한 것을 성취합니다.

public static string GetFriendlyTypeName(this Type t)
{
    var typeName = t.Name.StripStartingWith("`");
    var genericArgs = t.GetGenericArguments();
    if (genericArgs.Length > 0)
    {
        typeName += "<";
        foreach (var genericArg in genericArgs)
        {
            typeName += genericArg.GetFriendlyTypeName() + ", ";
        }
        typeName = typeName.TrimEnd(',', ' ') + ">";
    }
    return typeName;
}

public static string StripStartingWith(this string s, string stripAfter)
{
    if (s == null)
    {
        return null;
    }
    var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
    if (indexOf > -1)
    {
        return s.Substring(0, indexOf);
    }
    return s;
}

다음과 같이 사용하십시오.

[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
        .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}

이것은 당신이 찾고있는 것이 아니지만 관련 기술을 시연하는 데 도움이됩니다.


안녕하세요, 답변 주셔서 감사합니다. "StripStartingWith"에 대한 확장명도 추가 할 수 있습니까?
Cedric Arnould

1
@CedricArnould-추가되었습니다.
Ken Smith

5

그만큼 GetGenericArgument()메소드는 클래스 일반적인 클래스 인스턴스의 기본 유형 (설정해야합니다myClass<T> ). 그렇지 않으면, 타입을 돌려 준다 [0]

예:

Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();

5

다음과 같이 IEnumerable <T>를 구현하는 모든 컬렉션 유형에서 "T"유형을 얻을 수 있습니다.

public static Type GetCollectionItemType(Type collectionType)
{
    var types = collectionType.GetInterfaces()
        .Where(x => x.IsGenericType 
            && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        .ToArray();
    // Only support collections that implement IEnumerable<T> once.
    return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
}

IEnumerable <T>를 두 번 구현하는 컬렉션 유형은 지원하지 않습니다. 예 :

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }

이것을 지원 해야하는 경우 유형 배열을 반환 할 수 있다고 가정합니다 ...

또한이 작업을 많이 수행하는 경우 (예 : 루프) 컬렉션 유형별로 결과를 캐시 할 수도 있습니다.


1
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}

1
이것은 유형이 list-y 종류인지 여부에 대한 질문을 다루는 것처럼 보이지만, 목록으로 알려진 유형이 이미 초기화 된 일반 유형 매개 변수를 결정하는 방법에 대한 질문입니다.
Nathan Tuggy

1

3dGrabber의 솔루션 사용 :

public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();

0

속성의 기본 유형을 알고 싶다면 다음을 시도하십시오.

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

0

이것이 내가 한 방법입니다

internal static Type GetElementType(this Type type)
{
        //use type.GenericTypeArguments if exist 
        if (type.GenericTypeArguments.Any())
         return type.GenericTypeArguments.First();

         return type.GetRuntimeProperty("Item").PropertyType);
}

그럼 이렇게 불러

var item = Activator.CreateInstance(iListType.GetElementType());

또는

var item = Activator.CreateInstance(Bar.GetType().GetElementType());

-9

유형:

type = list.AsEnumerable().SingleOrDefault().GetType();

1
목록에 테스트 할 요소가없는 경우 NullReferenceException이 발생합니다.
rossisdead

1
SingleOrDefault()InvalidOperationException둘 이상의 요소 가 있을 때도 발생합니다.
독재자

이 답변은 \ @rossisdead와 \ @devgeezer가 올바르게 지적했듯이 잘못되었습니다.
올리버
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.