인스턴스화 된 System.Type을 제네릭 클래스의 형식 매개 변수로 전달


182

제목이 불분명합니다. 내가 알고 싶은 것은 이것이 가능한지입니다.

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

분명히 MyGenericClass는 다음과 같이 설명됩니다.

public class MyGenericClass<T>

현재 컴파일러는 '유형 또는 네임 스페이스'myType '을 찾을 수 없습니다'라고 불평합니다. "이를 수행 할 방법이 있어야합니다.


제네릭! = 템플릿. 모든 일반 유형 변수는 런타임이 아닌 컴파일 타임에 해결됩니다. 이것은 4.0의 '동적'유형이 유용 할 수있는 상황 중 하나입니다.

1
@ 윌-어떤 방법으로? 제네릭과 함께 사용하면 현재 CTP에서 본질적으로 <object> 버전을 호출하게됩니다 (트릭이 빠지지 않는 한 ...)
Marc Gravell

@MarcGravell 당신은 foo.Method((dynamic)myGenericClass)런타임 메소드 바인딩, 효과적으로 타입의 메소드 오버로드에 대한 서비스 로케이터 패턴에 사용할 수 있습니다 .
Chris Marisic

@ChrisMarisic 네, 몇 가지 일반적인 public void Method<T>(T obj)-그 트릭 이후 지난 6 년 동안 몇 번 이상 사용했던 트릭; p
Marc Gravell

@MarcGravell 메소드가 인스턴스화하도록 수정하는 방법이 있습니까?
barlop

답변:


220

당신은 반사없이 이것을 할 수 없습니다. 그러나 반사로 할 수 있습니다 . 다음은 완전한 예입니다.

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        Type typeArgument = Type.GetType(typeName);

        Type genericClass = typeof(Generic<>);
        // MakeGenericType is badly named
        Type constructedClass = genericClass.MakeGenericType(typeArgument);

        object created = Activator.CreateInstance(constructedClass);
    }
}

참고 : 일반 클래스가 여러 유형을 허용하는 경우 유형 이름을 생략 할 때 쉼표를 포함해야합니다. 예를 들면 다음과 같습니다.

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);

1
좋아, 이것은 좋지만 생성 된 메소드를 호출하는 방법은 무엇입니까? 더 많은 반사?
Robert C. Barth

7
제네릭 형식이 제네릭이 아닌 인터페이스를 구현하도록 할 수 있다면 해당 인터페이스로 캐스트 할 수 있습니다. 또는 제네릭으로 수행하려는 모든 작업을 수행하는 고유 한 제네릭 메서드를 작성 하고 리플렉션으로 호출 할 수도 있습니다.
Jon Skeet

1
네, 반환 된 유형에 대한 유일한 정보가 typeArgument 유형 변수에있는 경우 created 사용 방법을 이해하지 못합니까? 그것은 당신이 그 변수에 캐스팅 해야하는 것처럼 보이지만, 그것이 무엇인지 알지 못합니다. 반사로 그렇게 할 수 있는지 확실하지 않습니다. 예를 들어 List <int>와 같이 객체 변수로 객체 변수를 전달하면 객체가 int 유형의 경우 다른 질문이 있습니까? 작성된 변수가 int로 취급됩니까?
theringostarrs

6
@ RobertC.Barth "object"대신 "dynamic"예제 유형에서 "created"개체를 만들 수도 있습니다. 그렇게하면 메소드를 호출 할 수 있으며 런타임까지 평가가 연기됩니다.
McGarnagle

4
@balanza : MakeGenericMethod를 사용합니다.
Jon Skeet

14

불행히도 없습니다. 일반 인수는 컴파일시 1) 유효한 유형 또는 2) 다른 일반 매개 변수로 해석 할 수 있어야합니다. 리플렉션을 사용하지 않고도 런타임 값을 기반으로 일반 인스턴스를 만들 수있는 방법이 없습니다.


2

가위 코드로 실행하는 몇 가지 추가 방법. 비슷한 클래스가 있다고 가정하십시오.

public class Encoder() {
public void Markdown(IEnumerable<FooContent> contents) { do magic }
public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
}

런타임에 FooContent 가 있다고 가정하십시오.

컴파일 타임에 바인딩 할 수 있다면

var fooContents = new List<FooContent>(fooContent)
new Encoder().Markdown(fooContents)

그러나 런타임에는이 작업을 수행 할 수 없습니다. 런타임에이를 수행하려면 다음 라인을 수행하십시오.

var listType = typeof(List<>).MakeGenericType(myType);
var dynamicList = Activator.CreateInstance(listType);
((IList)dynamicList).Add(fooContent);

동적으로 호출하려면 Markdown(IEnumerable<FooContent> contents)

new Encoder().Markdown( (dynamic) dynamicList)

dynamic메소드 호출에서 의 사용법에 유의하십시오 . 런타임시 dynamicList됩니다 List<FooContent>(추가도되고 IEnumerable<FooContent>동적조차 사용은 여전히 런타임 바인더는 적절한 선택하는 강력한 형식의 언어에 뿌리를두고 있기 때문에) Markdown방법을. 정확히 일치하는 유형이 없으면 객체 매개 변수 메소드를 찾고 런타임 바인더 예외와 일치하지 않으면 메소드가 일치하지 않는다는 경고를 발생시킵니다.

이 접근 방식의 명백한 단점은 컴파일 타임에 유형 안전이 크게 손실된다는 것입니다. 그럼에도 불구하고 이러한 행을 따르는 코드를 사용하면 런타임에 예상대로 여전히 완전히 입력되었음을 매우 역동적으로 이해할 수 있습니다.


2

내 요구 사항이 약간 다르지만 희망적으로 누군가를 도울 것입니다. 구성에서 유형을 읽고 일반 유형을 동적으로 인스턴스화해야했습니다.

namespace GenericTest
{
    public class Item
    {
    }
}

namespace GenericTest
{
    public class GenericClass<T>
    {
    }
}

마지막으로 여기에 전화하는 방법이 있습니다. 백틱으로 유형을 정의하십시오 .

var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
var a = Activator.CreateInstance(t);

0

어떤 타입이 전달 될지 안다면, 이것을 반영하지 않고 이것을 할 수 있습니다. 스위치 문이 작동합니다. 분명히 이것은 제한된 수의 경우에만 작동하지만 리플렉션보다 훨씬 빠릅니다.

public class Type1 { }

public class Type2 { }

public class Generic<T> { }

public class Program
{
    public static void Main()
    {
        var typeName = nameof(Type1);

        switch (typeName)
        {
            case nameof(Type1):
                var type1 = new Generic<Type1>();
                // do something
                break;
            case nameof(Type2):
                var type2 = new Generic<Type2>();
                // do something
                break;
        }
    }
}

100 개의 클래스를 다루기 시작하면 추악 해집니다.
michael g

0

이 스 니펫에서는 동적으로 작성된 목록을 작성하고 사용하는 방법을 보여 드리고자합니다. 예를 들어 여기 동적 목록에 추가하고 있습니다.

void AddValue<T>(object targetList, T valueToAdd)
{
    var addMethod = targetList.GetType().GetMethod("Add");
    addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
}

var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
var list = Activator.CreateInstance(listType);

AddValue(list, 5);

마찬가지로 목록에서 다른 방법을 호출 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.