在C#中,如果我的对象支持该接口,我如何检查T是否为IInterface类型并转换为该类型?

在C#中,我有一个使用generics传入T的函数,我想运行检查以查看T是否是实现interfaceobject ,如果是,则调用该interface上的methods之一。

我不希望T约束只是那个Type。 是否有可能做到这一点?

例如:

 public class MyModel : IModel where T : MyObjectBase { public IQueryable GetRecords() { var entities = Repository.Query(); if (typeof(IFilterable).IsAssignableFrom(typeof(T))) { //Filterme is a method that takes in IEnumerable entities = FilterMe(entities)); } return entities; } public IEnumerable FilterMe(IEnumerable linked) where TResult : IFilterable { var dict = GetDict(); return linked.Where(r => dict.ContainsKey(r.Id)); } } 

我得到的错误是:

错误21类型’TResult’不能在generics类型或方法’FilterMe(System.Collections.Generic.IEnumerable)’中用作类型参数’TResult’。 没有从’TResult’到’IFilterable’的隐式引用转换。

丢失的部分是Cast<>()

 if(typeof(IFilterable).IsAssignableFrom(typeof(T))) { entities = FilterMe(entities.Cast()).AsQueryable().Cast(); } 

注意使用Cast<>()将实体列表转换为正确的子类型。 除非T实现IFilterable ,否则此IFilterable将失败,但由于我们已经检查过,我们知道它会。

 if (typeof(IMyInterface).IsAssignableFrom(typeof(T)) 

这将检查是否可以从类型T的实例分配IMyInterface类型的变量。

如果你有一个generics类型的参数可能会或可能不会实现IFoo ,可以将其转换为IFoo类型的存储位置; 如果你这样做,你可以将它传递给任何期望IFoo方法,以及任何期望generics参数约束到IFoo ,但如果你这样做,你将丢失所有generics类型信息 – 参数将被传递作为类型IFoo 。 除此之外,这意味着如果您的原始对象是一个结构,它将被装箱。

如果您希望测试generics参数类型是否实现IFoo并调用一个采用generics约束IFoo如果有),同时保留原始generics类型(如果类型是结构,这可能是有用的,如果该类型被传递给一个generics方法,该方法同时具有IFooIBar约束,并且可能想要传递的东西不共享任何单个常见的超类型,因此需要使用Reflection。

例如,假设有人想要一个方法Zap ,它接受一个通用的ref参数,如果它实现了IDisposable则调用Dispose ,并清除它。 如果参数是IDisposable类类型,则应将null测试作为primefaces操作执行,并清除参数。

 public static class MaybeDisposer { static class ClassDisposer where T : class,IDisposable { public static void Zap(ref T it) { T old_it = System.Threading.Interlocked.Exchange(ref it, null); if (old_it != null) { Console.WriteLine("Disposing class {0}", typeof(T)); old_it.Dispose(); } else Console.WriteLine("Class ref {0} already null", typeof(T)); } } static class StructDisposer where T : struct,IDisposable { public static void Zap(ref T it) { Console.WriteLine("Disposing struct {0}", typeof(T)); it.Dispose(); it = default(T); } } static class nonDisposer { public static void Zap(ref T it) { Console.WriteLine("Type {0} is not disposable", typeof(T)); it = default(T); } } class findDisposer { public static ActByRef Zap = InitZap; public static void InitZap(ref T it) { Type[] types = {typeof(T)}; Type t; if (!(typeof(IDisposable).IsAssignableFrom(typeof(T)))) t = typeof(MaybeDisposer.nonDisposer<>).MakeGenericType(types); else if (typeof(T).IsValueType) t = typeof(MaybeDisposer.StructDisposer<>).MakeGenericType(types); else t = typeof(MaybeDisposer.ClassDisposer<>).MakeGenericType(types); Console.WriteLine("Assigning disposer {0}", t); Zap = (ActByRef)Delegate.CreateDelegate(typeof(ActByRef), t, "Zap"); Zap(ref it); } } public static void Zap(ref T it) { findDisposer.Zap(ref it); } } 

第一次使用任何类型T调用代码时,它将确定可以为该参数生成哪种通用静态类,并使用Reflection创建委托以调用该generics类的静态方法。 具有相同类型T后续调用将使用缓存的委托。 尽管reflection可能有点慢,但只需要对任何类型的T使用一次。 所有后续对具有相同类型T MaybeDisposer.Zap(ref T it)调用将直接通过委托调度,因此将快速执行。

请注意,如果给出了不符合给定开放generics类的约束的generics类型参数,则对MakeGenericType的调用将抛出exception(例如,如果T是类或未实现IDisposable ,则尝试生成generics类型StructDisposer会抛出exception); 此类测试在运行时进行,并且未由编译器validation,因此您可以使用运行时检查来查看类型是否满足适当的约束。 请注意,代码不会测试it是否实现IDisposable ,而是测试T是否实现。 这是非常重要的。 否则,如果使用Object类型的参数调用MaybeDispose来保存对Stream的引用,则它将确定it实现了IDisposable并因此尝试创建ClassDisposer ,因为Object没有实现IDisposable崩溃。

我能想出的最简单的forms是:

 public IEnumerable GetRecords() { IQueryable entities = new List().AsQueryable(); if (typeof(IFilterable).IsAssignableFrom(typeof(T))) { entities = FilterMe(entities.OfType()).AsQueryable(); } return entities; } public IEnumerable FilterMe(IEnumerable linked) where TSource : IFilterable { return linked.Where(r => true).OfType(); } 

这里的要点是需要让类型传入和退出方法。 我不得不在本地更改类型以使其正常工作。

OfType将静默过滤掉实际上不属于给定类型的项目,因此它假定它是任何一个调用中相同类型的集合。

由于您要从FilterMe重新分配,因此仍需要接口可分配检查。

OfType(...)方法( 链接 )你在寻找什么?

 public class MyModel : IModel where T : MyObjectBase { public IQueryable GetRecords() { var entities = Repository.Query(); if (typeof(IFilterable).IsAssignableFrom(typeof(T))) { //Filterme is a method that takes in IEnumerable entities = FilterMe(entities)); } return entities; } public IEnumerable FilterMe(IEnumerable linked) where TResult : IFilterable { var dict = GetDict(); return linked.OfType().Where(r => dict.ContainsKey(r.Id)); } } 

在我的回答中,我假设FilterMe方法在内部使用,不应该在模型外部可见,并且可以标记为private 。 如果我的假设是错误的,您可以创建FilterMe的私有重载。

在我的回答中,我刚删除了generics 。 我假设这个FilterMe总是大约是T类型的实体(因为它在同一个Model类中)。 这解决了TTResult之间的铸造问题。 由于没有使用IFilterable的成员,因此IFilterableTResult标记为IFilterable 。 并且由于代码已经检查T是否为IFilterable为什么再次检查(特别是当FilterMe是私有的时)?

  public IQueryable GetRecords() { var entities = Repository.Query(); if (typeof(IFilterable).IsAssignableFrom(typeof(T))) { //Filterme is a method that takes in IEnumerable entities = FilterMe(entities).AsQueryable(); } return entities; } public IEnumerable FilterMe(IEnumerable linked) { var dict = GetDict(); return linked.Where(r => dict.ContainsKey(r.Id)); } 

如果要创建第二个FilterMe ,请使用Queryable替换IEumerable类型,这样就不必使用AsQueryable()转换实体。

 public IEnumerable FilterMe(IEnumerable linked) where TResult : IFilterable { var dict = GetDict(); return linked.Where(r => dict.ContainsKey(r.Id)); } 

尝试使用此版本替换FilterMe:

 public IEnumerable FilterMe(IEnumerable linked) { var dict = GetDict(); return linked.Where(r => dict.ContainsKey(r.Id)).Cast(); } 

然后,你打电话,把你的代码更改为:

 if (typeof(IFilterable).IsAssignableFrom(typeof(T))) { //Filterme is a method that takes in IEnumerable var filterable = entities.Cast(); entities = FilterMe(entities).AsQueryable(); } 

您不必使FilterMe方法成为通用方法即可获得相同的结果。

  public class MyModel : IModel where T : MyObjectBase { public IQueryable GetRecords() { var entities = Repository.Query(); if (typeof(IFilterable).IsAssignableFrom(typeof(T))) { //Filterme is a method that takes in IEnumerable entities = FilterMe(entities.Cast()); } return entities; } public IEnumerable FilterMe(IEnumerable linked) { var dict = GetDict(); return linked .Where(r => dict.ContainsKey(r.Id)) .Cast(); } }