在 C# 中,如果我的对象支持该接口,我如何检查 T 是否属于 IInterface 类型并强制转换为该类型
本文关键字:类型 IInterface 属于 是否 转换 检查 我的 对象 如果 支持 接口 | 更新日期: 2023-09-27 18:10:51
在 C# 中,我有一个使用 generics
传入T
的函数,我想运行检查以查看T
是否是实现interface
的object
,如果是,则调用该interface
上的methods
之一。
我不想T
约束只属于这种类型。 可以这样做吗?
例如:
public class MyModel<T> : IModel<T> where T : MyObjectBase
{
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities));
}
return entities;
}
public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
}
我得到的错误是:
错误 21 类型"TResult"不能用作泛型类型或方法"FilterMe(System.Collections.Generic.IEnumerable("中的类型参数"TResult"。没有从"TResult"到"IFilterable"的隐式引用转换。
缺少的部分是Cast<>()
:
if(typeof(IFilterable).IsAssignableFrom(typeof(T))) {
entities = FilterMe(entities.Cast<IFilterable>()).AsQueryable().Cast<T>();
}
请注意使用 Cast<>()
将实体列表转换为正确的子类型。除非T
实现IFilterable
,否则此强制转换将失败,但由于我们已经检查过,我们知道它会失败。
if (typeof(IMyInterface).IsAssignableFrom(typeof(T))
这将检查是否可以从类型 T
的实例中分配 IMyInterface
类型的变量。
如果你有一个泛型类型的参数,可能会也可能不会实现IFoo
,则可以as
将其强制转换为 IFoo
类型的存储位置; 如果你这样做,你可以把它传递给任何需要IFoo
的方法,以及任何期望泛型参数约束IFoo
的方法, 但是如果这样做,您将丢失所有泛型类型信息 - 参数将作为类型 IFoo
传递。 除此之外,这意味着如果您的原始对象是一个结构,它将被装箱。
如果您希望测试泛型参数类型是否实现IFoo
并调用一个采用泛型约束IFoo
的方法(如果它确实如此(,同时保留原始泛型类型(如果类型是结构,这可能很有用,并且如果类型被传递给具有IFoo
和IBar
约束的泛型方法,并且可能想要传递的东西没有共享任何单个公共超类型(,有必要使用反射。
例如,假设想要一个方法Zap
它接受一个泛型ref
参数,如果它实现了IDisposable
,则调用Dispose
,并将其清除。 如果参数是IDisposable
类类型,则应将 null 测试作为原子操作执行,并清除参数。
public static class MaybeDisposer
{
static class ClassDisposer<T> 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<T> 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<T>
{
public static void Zap(ref T it)
{
Console.WriteLine("Type {0} is not disposable", typeof(T));
it = default(T);
}
}
class findDisposer<T>
{
public static ActByRef<T> 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<T>)Delegate.CreateDelegate(typeof(ActByRef<T>), t, "Zap");
Zap(ref it);
}
}
public static void Zap<T>(ref T it)
{
findDisposer<T>.Zap(ref it);
}
}
第一次使用任何类型的T
调用代码时,它将确定可以为该参数生成哪种泛型静态类,并使用 Reflection 创建一个委托来调用该泛型类的静态方法。 具有相同类型T
的后续调用将使用缓存的委托。 尽管反射可能有点慢,但它只需要为任何类型的T
使用一次。 所有后续对具有相同类型T
MaybeDisposer.Zap<T>(ref T it)
的调用都将直接通过委托调度,从而快速执行。
类型参数不符合给定开放泛型类的约束(例如,如果T
是一个类或没有实现IDisposable
,则对MakeGenericType
的调用将引发异常,尝试使泛型类型StructDisposer<T>
将引发异常(;此类测试在运行时发生,未经编译器验证, 因此,您可以使用运行时检查来查看类型是否满足适当的约束。 请注意,代码不测试it
是否实现IDisposable
,而是测试T
是否实现。 这一点非常重要。 否则,如果使用Object
类型的参数调用MaybeDispose
,该参数包含对Stream
的引用,它将确定it
实现IDisposable
,从而尝试创建一个ClassDisposer<Object>
,因为Object
没有实现IDisposable
而崩溃。
我能想到的最简单的形式是这样的:
public IEnumerable<T> GetRecords()
{
IQueryable<T> entities = new List<T>().AsQueryable();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
entities = FilterMe<IFilterable, T>(entities.OfType<IFilterable>()).AsQueryable();
}
return entities;
}
public IEnumerable<TResult> FilterMe<TSource, TResult>(IEnumerable<TSource> linked) where TSource : IFilterable
{
return linked.Where(r => true).OfType<TResult>();
}
这里的重点是需要有类型传入和返回方法。我不得不在本地更改类型才能使其正常工作。
OfType
将以静默方式筛选出不属于真正给定类型的项,因此它假定它是任何一个调用中相同类型的集合。
因为您是从 FilterMe
重新分配的,所以您仍然需要接口可分配检查。
OfType(...)
方法(链接(是您要查找的吗?
public class MyModel<T> : IModel<T> where T : MyObjectBase
{
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities));
}
return entities;
}
public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.OfType<TResult>().Where(r => dict.ContainsKey(r.Id));
}
}
在我的回答中,我假设方法 FilterMe
在内部使用,不应该在您的模型外部可见,并且可以标记为private
.如果我的假设是错误的,您可以创建FilterMe
的私人重载。
在我的回答中,我只是删除了通用<TResult>
.我假设这个FilterMe
总是关于 T
类型的实体(因为它在同一个 Model 类中(。这解决了T
和TResult
之间的铸造问题。 TResult
不必标记为IFilterable
,因为不使用IFilterable
的任何成员。既然代码已经检查了T
是否IFilterable
为什么要再次检查(尤其是当FilterMe
是私有的时(?
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities).AsQueryable();
}
return entities;
}
public IEnumerable<T> FilterMe(IEnumerable<T> linked)
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
如果要创建第二个FilterMe
,请将IEumerable<T>
类型替换为 Queryable<T>
,因此您不必使用 AsQueryable()
转换实体。
public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
尝试将 FilterMe 替换为此版本:
public IEnumerable<T> FilterMe(IEnumerable<IFilterable> linked)
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id)).Cast<T>();
}
然后,如果你打电话,把你的代码改成这样:
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
var filterable = entities.Cast<IFilterable>();
entities = FilterMe(entities).AsQueryable();
}
不必使FilterMe
方法成为泛型方法即可获得相同的结果。
public class MyModel<T> : IModel<T> where T : MyObjectBase {
public IQueryable<T> GetRecords()
{
var entities = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
//Filterme is a method that takes in IEnumerable<IFilterable>
entities = FilterMe(entities.Cast<IFilterable>());
}
return entities;
}
public IEnumerable<T> FilterMe(IEnumerable<IFilterable> linked) {
var dict = GetDict();
return linked
.Where(r => dict.ContainsKey(r.Id))
.Cast<T>();
}
}