Bir türün belirli bir genel arabirim türünü uygulayıp uygulamadığı nasıl belirlenir


226

Aşağıdaki tür tanımlarını varsayalım:

public interface IFoo<T> : IBar<T> {}
public class Foo<T> : IFoo<T> {}

Yalnızca karıştırılmış tür kullanılabilir olduğunda türün Foogenel arabirimi uygulayıp uygulamadığını nasıl öğrenebilirim IBar<T>?

Yanıtlar:


387

TcKs yanıt kullanarak aşağıdaki LINQ sorgusu ile de yapılabilir:

bool isBar = foo.GetType().GetInterfaces().Any(x =>
  x.IsGenericType &&
  x.GetGenericTypeDefinition() == typeof(IBar<>));

1
Bu çok zarif bir çözüm! SO üzerinde gördüğüm diğerleri foreach döngüleri veya daha uzun LINQ sorguları kullanır. Bunu kullanmak için .NET framework 3.5'e sahip olmanız gerektiğini unutmayın.
Daniel T.

7
Bunu la bit.ly/ccza8B bir uzatma yöntemi yapmanızı tavsiye ederim - bunu oldukça güzel temizleyecek!
Brad Heller

1
İhtiyaçlarınıza bağlı olarak, iade edilen arayüzlerde tekrarlamanız gerektiğini görebilirsiniz.
Sebastian Good

4
Bunun .net içinde çok daha iyi ... çekirdek gibi ... uygulanması gerektiğini söyleyebilirim. .. c # keşfetmek ve şu anda .net daha sonra biraz hayal kırıklığına uğradım ...
Sofija

2
küçük ekleme: IBar'ın birden fazla genel türü varsa, bunu belirtmeniz gerekir: typeof(IBar<,,,>)virgüllerin yer tutucu gibi davranması
Rob Von Nesselrode

33

Kalıtım ağacından yukarı çıkmanız ve ağaçtaki her sınıf için tüm arayüzleri bulmanız ve arayüzün genel olup olmadığıtypeof(IBar<>) çağrısının sonucuyla karşılaştırmanız gerekir . Kesinlikle biraz acı verici.Type.GetGenericTypeDefinition

Bkz bu cevabı ve bu olanları daha fazla bilgi ve kod için.


neden sadece IBar <SomeClass> 'a yayın yapıp null olup olmadığını kontrol etmiyorsunuz? (Tabii ki 'as' ile döküm demek)
Pablo Retyk

5
T bilinmiyor ve belirli bir türe dönüştürülemiyor.
sduplooy

@sduplooy: Belki bir şey eksik T nasıl bilinmez? kamu sınıfını derleyecekti Foo: IFoo <T> {}
Pablo Retyk

25
public interface IFoo<T> : IBar<T> {}
public class Foo : IFoo<Foo> {}

var implementedInterfaces = typeof( Foo ).GetInterfaces();
foreach( var interfaceType in implementedInterfaces ) {
    if ( false == interfaceType.IsGeneric ) { continue; }
    var genericType = interfaceType.GetGenericTypeDefinition();
    if ( genericType == typeof( IFoo<> ) ) {
        // do something !
        break;
    }
}

2
Typeof (Foo), bir System.Type nesnesini (Foo'yu açıklayan) döndürdüğünden, GetType () çağrısı her zaman System.Type türünü döndürür. Tipeof (Foo) olarak değiştirmelisiniz. GetInterfaces ()
Michael Meadows

9

Yardımcı yöntem uzantısı olarak

public static bool Implements<I>(this Type type, I @interface) where I : class
{
    if(((@interface as Type)==null) || !(@interface as Type).IsInterface)
        throw new ArgumentException("Only interfaces can be 'implemented'.");

    return (@interface as Type).IsAssignableFrom(type);
}

Örnek kullanım:

var testObject = new Dictionary<int, object>();
result = testObject.GetType().Implements(typeof(IDictionary<int, object>)); // true!

2
"IsAssignableFrom" tam olarak aradığım şeydi - teşekkürler
Jesper

22
Bu, istemcinin genel tür parametresini bilmeme gereksinimi için çalışmaz. Örneğin testObject.GetType (). Implements (typeof (IDictionary <,>)); yanlış döndürür.
ctusch

@ctusch, bunun için bir çözüm var mı?
Tohid

5

@GenericProgrammers uzantı yönteminin biraz daha basit bir sürümünü kullanıyorum:

public static bool Implements<TInterface>(this Type type) where TInterface : class {
    var interfaceType = typeof(TInterface);

    if (!interfaceType.IsInterface)
        throw new InvalidOperationException("Only interfaces can be implemented.");

    return (interfaceType.IsAssignableFrom(type));
}

Kullanımı:

    if (!featureType.Implements<IFeature>())
        throw new InvalidCastException();

5
Hala jenerik arayüzler için orijinal sorunun gereksinimine göre çalışmıyor.
nathanchere

4

Yapılmış bir tür genel arabirime karşı kontrol etmeniz gerekir.

Bunun gibi bir şey yapmanız gerekecek:

foo is IBar<String>

çünkü IBar<String>oluşturulmuş türü temsil eder. Çünkü eğer bunu yapmak zorunda sebebi Tçekinize tanımlanmamış demek istiyorsan, derleyici bilmiyor IBar<Int32>ya IBar<SomethingElse>.


4

Tamamen tip sistemini mücadele için, sana kolu özyineleme, örneğin gerek IList<T>: ICollection<T>: IEnumerable<T>Bunu bilemeyiz onsuz, IList<int>sonuçta uygular IEnumerable<>.

    /// <summary>Determines whether a type, like IList&lt;int&gt;, implements an open generic interface, like
    /// IEnumerable&lt;&gt;. Note that this only checks against *interfaces*.</summary>
    /// <param name="candidateType">The type to check.</param>
    /// <param name="openGenericInterfaceType">The open generic type which it may impelement</param>
    /// <returns>Whether the candidate type implements the open interface.</returns>
    public static bool ImplementsOpenGenericInterface(this Type candidateType, Type openGenericInterfaceType)
    {
        Contract.Requires(candidateType != null);
        Contract.Requires(openGenericInterfaceType != null);

        return
            candidateType.Equals(openGenericInterfaceType) ||
            (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition().Equals(openGenericInterfaceType)) ||
            candidateType.GetInterfaces().Any(i => i.IsGenericType && i.ImplementsOpenGenericInterface(openGenericInterfaceType));

    }

3

Her şeyden önce public class Foo : IFoo<T> {}derlenmez çünkü T yerine bir sınıf belirtmeniz gerekir, ancak böyle bir şey yaptığınızı varsayarsakpublic class Foo : IFoo<SomeClass> {}

o zaman yaparsan

Foo f = new Foo();
IBar<SomeClass> b = f as IBar<SomeClass>;

if(b != null)  //derives from IBar<>
    Blabla();

2

Genel taban türlerini ve arabirimleri destekleyecek bir uzantı yöntemi istemeniz durumunda, sduplooy'un yanıtını genişlettim:

    public static bool InheritsFrom(this Type t1, Type t2)
    {
        if (null == t1 || null == t2)
            return false;

        if (null != t1.BaseType &&
            t1.BaseType.IsGenericType &&
            t1.BaseType.GetGenericTypeDefinition() == t2)
        {
            return true;
        }

        if (InheritsFrom(t1.BaseType, t2))
            return true;

        return
            (t2.IsAssignableFrom(t1) && t1 != t2)
            ||
            t1.GetInterfaces().Any(x =>
              x.IsGenericType &&
              x.GetGenericTypeDefinition() == t2);
    }

1

Türün genel bir türü miras alıp almadığını kontrol etme yöntemi:

   public static bool IsTheGenericType(this Type candidateType, Type genericType)
    {
        return
            candidateType != null && genericType != null &&
            (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition() == genericType ||
             candidateType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == genericType) ||
             candidateType.BaseType != null && candidateType.BaseType.IsTheGenericType(genericType));
    }

0

Aşağıdaki uzantıyı deneyin.

public static bool Implements(this Type @this, Type @interface)
{
    if (@this == null || @interface == null) return false;
    return @interface.GenericTypeArguments.Length>0
        ? @interface.IsAssignableFrom(@this)
        : @this.GetInterfaces().Any(c => c.Name == @interface.Name);
}

Test etmek için. oluşturmak

public interface IFoo { }
public interface IFoo<T> : IFoo { }
public interface IFoo<T, M> : IFoo<T> { }
public class Foo : IFoo { }
public class Foo<T> : IFoo { }
public class Foo<T, M> : IFoo<T> { }
public class FooInt : IFoo<int> { }
public class FooStringInt : IFoo<string, int> { }
public class Foo2 : Foo { }

ve test yöntemi

public void Test()
{
    Console.WriteLine(typeof(Foo).Implements(typeof(IFoo)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<>)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<int>)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<string>)));
    Console.WriteLine(typeof(FooInt).Implements(typeof(IFoo<,>)));
    Console.WriteLine(typeof(FooStringInt).Implements(typeof(IFoo<,>)));
    Console.WriteLine(typeof(FooStringInt).Implements(typeof(IFoo<string,int>)));
    Console.WriteLine(typeof(Foo<int,string>).Implements(typeof(IFoo<string>)));
 }

-2

Aşağıdakiler yanlış bir şey olmamalıdır:

bool implementsGeneric = (anObject.Implements("IBar`1") != null);

IBar sorgunuzla belirli bir genel tip parametresi sağlamak istiyorsanız, ek kredi için AmbiguousMatchException yakalayabilirsiniz.


Mümkün olduğunda dize değişmezlerini kullanmaktan kaçınmak genellikle daha iyidir. Bu yaklaşım, uygulamayı yeniden düzenlemeyi zorlaştırır, çünkü IBar arabirimini yeniden adlandırmak dize değişmezini değiştirmez ve hata yalnızca çalışma zamanında algılanabilir.
andyroschy

Ben genellikle 'sihirli dizeleri' vb kullanarak yukarıdaki yorum katılıyorum kadar, bu hala bulduğum en iyi yaklaşımdır. Yeterince yakın - PropertyType.Name eşittir "IWhatever`1" için test.
nathanchere

Neden olmasın? bool implementsGeneric = (anObject.Implements(typeof(IBar<>).Name) != null);
Maxime Gélinas
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.