C # 'da nesnenin genel tipte olup olmadığını test etme


134

Bir nesne genel türdeyse bir test yapmak istiyorum. Aşağıdakileri başarılı olmadan denedim:

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

Neyi yanlış yapıyorum ve bu testi nasıl yaparım?

Yanıtlar:


201

Bunun genel bir türün örneği olup olmadığını kontrol etmek istiyorsanız:

return list.GetType().IsGenericType;

Genel olup olmadığını kontrol etmek istiyorsanız List<T>:

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

Jon'un belirttiği gibi, bu tam tip denkliğini kontrol eder. Dönen falseanlamına gelmez list is List<T>döner false(yani amacı, bir tahsis edilemez List<T>değişken).


9
Ancak bu alt tipleri algılamaz. Cevabımı gör. Arayüzler için de çok daha zor :(
Jon Skeet

1
Genel bir tür değilse GetGenericTypeDefinition çağrısı atılır. Önce bunu kontrol ettiğinizden emin olun.
Kilhoffer

85

Sadece türün genel olup olmadığını bilmek istemiyorum, ancak bir nesne türü bağımsız değişkenleri bilmeden belirli bir genel türün bir örneği olup olmadığını bilmek istiyorum.

Ne yazık ki çok basit değil. Genel tür bir sınıfsa (bu durumda olduğu gibi) çok kötü değil, ancak arabirimler için daha zordur. İşte bir sınıfın kodu:

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>
    {
    }
}

EDIT: Yorumlarda belirtildiği gibi, bu arabirimler için işe yarayabilir:

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

Bu konuda bazı garip kenar vakaları olabilir sinsi bir şüphem var, ama şu anda başarısız birini bulamıyorum.


2
Sadece bununla ilgili bir sorun keşfettim. Sadece tek bir miras çizgisine iner. , Yol boyunca, bir temel sınıf hem bir üs varsa ve aradığınız arayüzüne bu yalnızca sınıf yolunu iner.
Groxx

1
@Groxx: Doğru. Sadece cevapta bahsettiğimi fark ettim: "Genel tip bir sınıf (bu durumda olduğu gibi) ise çok kötü değil ama arayüzler için daha zor. İşte bir sınıf kodu"
Jon 19.Saret

1
<T> 'yi bilmenin bir yolu yoksa ne olur? Gibi int veya string olabilir, ama bunu bilmiyorsun. Bu, yanlış negatifler üretir ... yani kullanmak için bir T'niz yok, sadece bir nesnenin özelliklerini inceliyorsunuz ve biri bir liste. Listenin soyulmasını önlemek için bir liste olduğunu nereden biliyorsunuz? Bununla demek istediğim, hiçbir yerde bir T'niz veya kullanacağınız bir tür yok. Her türü tahmin edebilirsiniz (Liste <int>? Liste <string> mi?) Ama bilmek istediğiniz şey BU AA LİSTESİ Mİ? Bu soruya cevap vermek zor görünüyor.

@RiverC: Evet, haklısın - bu olduğunu çeşitli nedenlerle, cevaba oldukça zor. Sadece bir sınıftan bahsediyorsanız, bu çok kötü değil ... miras ağacında yürümeye devam edebilir ve bir şekilde mi yoksa başka bir şekilde mi vurduğunuzu görebilirsiniz List<T>. Arayüzler eklerseniz, bu gerçekten zor.
Jon Skeet

3
döngüyü eşitlik operatörü ( ) yerine IsInstanceOfGenericTypebir çağrıyla değiştiremez misiniz ? IsAssignableFrom==
slawekwin

7

Dinamik althougth kullanarak daha kısa kod kullanabilirsiniz, bu saf yansımadan daha yavaş olabilir:

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

Bunlar, genel tip kontrolünün en uç durumlarını kapsayan iki favori uzantı yöntemimdir:

İle çalışır:

  • Çoklu (genel) arayüzler
  • Çoklu (genel) temel sınıflar
  • True değerini döndürürse belirli genel türü 'dışarıda bırakacak' aşırı yüke sahiptir (örnekler için birim testine bakın):

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

İşte (temel) işlevselliği göstermek için bir test:

 [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
Farklı bir soru için doğru. Bu soru için, sorunun yalnızca yarısını (önemli ölçüde daha az) ele aldığı için yanlıştır.
Groxx

1
Stan R'nin cevabı aslında soruyu olduğu gibi cevaplıyor, ancak OP'nin gerçekte anlamı " C # ' da nesnenin belirli bir genel tipte olup olmadığını test etmek " idi, bu cevap gerçekten eksikti.
yoyo

insanlar bana "Oylama" yerine "jenerik bir tip" genel bir tip bağlamında cevap verdim çünkü aşağı oy veriyor. İngilizce benim 2. dilim ve bu dil nüansları beni sık sık geçiriyor, savunmam için OP özellikle belirli bir türe karşı test etmek istemedi ve başlıkta "genel tip ... neden aşağı oyları hak ettiğimi bilmiyorum belirsiz bir soru.
Stan R.

2
Artık bunu biliyorsunuz ve cevabınızı daha spesifik ve doğru olacak şekilde geliştirebilirsiniz.
Peter Ivan
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.