Genel Sınıf için Tür Parametresi Olarak Örneklendirilmiş Bir System.Type Verme


183

Başlık biraz belirsiz. Bilmek istediğim bu mümkün ise:

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

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

Açıkçası, MyGenericClass şöyle tanımlanır:

public class MyGenericClass<T>

Şu anda derleyici, 'myType türünün veya ad alanının bulunamadığından şikayet ediyor. "Bunu yapmanın bir yolu olmalı.


Jenerikler! = Şablonlar. Tüm genel tip değişkenleri çalışma zamanında değil derleme zamanında çözümlenir. 4.0'ın 'dinamik' türünün faydalı olabileceği durumlardan biri budur.

1
@Will - ne şekilde? Jeneriklerle birlikte kullanıldığında, mevcut CTP altında esasen <object> sürümlerini çağırırsınız (bir numarayı kaçırmadıkça ...)
Marc Gravell

@MarcGravell foo.Method((dynamic)myGenericClass), bir türün yöntem aşırı yüklemeleri için servis bulucu kalıbını etkin bir şekilde çalışma süresi yöntemi bağlama için kullanabilirsiniz .
Chris Marisic

@ChrisMarisic evet, bazı jenerikler için public void Method<T>(T obj)- bu yorumdan bu yana son 6 yılda birkaç kereden fazla kullandım; p
Marc Gravell

@MarcGravell, yöntemi başlatan şekilde değiştirmenin bir yolu var mı?
barlop

Yanıtlar:


220

Bunu yansıma olmadan yapamazsınız. Ancak, olabilir yansıma ile bunu. İşte tam bir örnek:

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);
    }
}

Not: genel sınıfınız birden çok türü kabul ederse, tür adlarını atlarken virgül eklemeniz gerekir; örneğin:

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

1
Tamam, bu iyi, ama nasıl çağrı yöntemleri hakkında oluşturulan oluşur? Daha fazla yansıma mı?
Robert C. Barth

7
Genel türünüzü genel olmayan bir arayüze dönüştürmekten kurtulabilirseniz, bu arayüze yayın yapabilirsiniz. Alternatif olarak, jenerik yapmak istiyorum işin hepsini yapıyor kendi jenerik yöntem yazmak ve diyebiliriz o yansıma ile.
Jon Skeet

1
Evet, döndürülen türü hakkında sahip olduğunuz tek bilgi typeArgument türü değişkeni ise oluşturulan nasıl kullanılacağını anlamıyorum? Bana öyle geliyor ki o değişkene atmak zorunda kalacaksın, ama bunun ne olduğunu bilmiyorsun, bunu yansıma ile yapıp yapamayacağından emin değilim. Nesne, örneğin int bir nesne değişkeni olarak geçerseniz, örneğin bir liste <int> bu iş için bir liste <int> varsa nesne int olup olmadığını başka bir soru? oluşturulan değişken int olarak değerlendirilecek mi?
theringostarrs

6
@ RobertC.Barth "Nesne" yerine "dinamik" örnek türünde "oluşturulan" nesneyi de yapabilirsiniz. Bu şekilde üzerinde yöntemler çağırabilirsiniz ve değerlendirme çalışma zamanına kadar ertelenir.
McGarnagle

4
@balanza: MakeGenericMethod kullanıyorsunuz.
Jon Skeet

14

Ne yazık ki hayır yok. Genel bağımsız değişkenler Derleme zamanında 1) geçerli bir tür veya 2) başka bir genel parametre olarak çözümlenebilir olmalıdır. Yansımayı kullanmanın büyük darbesi olmadan çalışma zamanı değerlerine dayalı genel örnekler oluşturmanın bir yolu yoktur.


2

Bazı ek makas kodu ile çalıştırmak için nasıl. Diyelim ki benzer bir sınıfınız var

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

Çalışma zamanında bir FooContent'iniz olduğunu varsayalım

Eğer derleme zamanında bağlamak mümkün olsaydı

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

Ancak bunu çalışma zamanında yapamazsınız. Çalışma zamanında bunu yapmak için aşağıdakiler boyunca yapardınız:

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

Dinamik olarak çağırmak için Markdown(IEnumerable<FooContent> contents)

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

dynamicYöntem çağrısında kullanımını not edin . Dinamik kullanım bile güçlü bir şekilde yazılmış bir dile kök saldığından çalışma zamanında (ek olarak da dynamicListolacaktır ) çalışma süresi bağlayıcı uygun yöntemi seçecektir . Tam tür eşleşmesi yoksa, bir nesne parametresi yöntemi arar ve hiçbir eşleşme eşleşmezse, hiçbir yöntem eşleşmediğini bildiren bir çalışma zamanı bağlayıcı istisnası yükseltilmez.List<FooContent>IEnumerable<FooContent>Markdown

Bu yaklaşıma bariz bir geri dönüş, derleme zamanında büyük bir tip güvenlik kaybıdır. Bununla birlikte, bu satırlar boyunca kod, çalışma zamanında beklediğiniz gibi hala tam olarak yazıldığı çok dinamik bir anlamda çalışmanıza izin verecektir.


2

Gereksinimlerim biraz farklıydı, ama umarım birine yardım eder. Bir yapılandırma türünü okumak ve genel tür dinamik olarak somutlaştırmak gerekiyordu.

namespace GenericTest
{
    public class Item
    {
    }
}

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

Son olarak, işte böyle diyorsunuz. Türü bir ters tırnak ile tanımlayın .

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

0

Hangi türlerin geçeceğini biliyorsanız, bunu yansıtmadan yapabilirsiniz. Bir anahtar deyimi işe yarayacaktır. Açıkçası, bu sadece sınırlı sayıda durumda işe yarayacaktır, ancak yansımadan çok daha hızlı olacaktır.

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'lü sınıflarla uğraşmaya başladıktan sonra bu çirkin olur.
michael g

0

Bu snippet'te dinamik olarak oluşturulmuş bir listenin nasıl oluşturulacağını ve kullanılacağını göstermek istiyorum. Örneğin, buradaki dinamik listeye ekliyorum.

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);

Benzer şekilde listedeki başka herhangi bir yöntemi de çağırabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.