在C#中,如果我的对象支持该接口,我如何检查T是否为IInterface类型并转换为该类型?
在C#中,我有一个使用generics
传入T
的函数,我想运行检查以查看T
是否是实现interface
的object
,如果是,则调用该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方法,该方法同时具有IFoo
和IBar
约束,并且可能想要传递的东西不共享任何单个常见的超类型,因此需要使用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
调用将直接通过委托调度,因此将快速执行。
请注意,如果给出了不符合给定开放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类中)。 这解决了T
和TResult
之间的铸造问题。 由于没有使用IFilterable
的成员,因此IFilterable
将TResult
标记为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(); } }