c#测试对象是否实现了ISurface<;T>;使用.GetType().GetInterface(typeof(ISurface<;).FullName)的任何类型

c#测试对象是否实现了ISurface<;T>;使用.GetType().GetInterface(typeof(ISurface<;).FullName)的任何类型,c#,linq,generics,interface,C#,Linq,Generics,Interface,我有一个插件架构,需要检测实现任何类型的接口ISurface(即测试ISurface)的所有插件。我看到这里有几个使用LINQ的建议(例如),我想知道是否有理由支持这一点: .GetType().GetInterface("ISurface`1") 编辑:关于对接口名称进行硬编码,我认为如果直接从实际接口提取名称,这些问题将得到缓解,正如Tim在下面提到的: .GetType().GetInterface(typeof(ISurface).FullName) 使用.FullName时,名称空

我有一个插件架构,需要检测实现任何类型的接口
ISurface
(即测试
ISurface
)的所有插件。我看到这里有几个使用LINQ的建议(例如),我想知道是否有理由支持这一点:

.GetType().GetInterface("ISurface`1")
编辑:关于对接口名称进行硬编码,我认为如果直接从实际接口提取名称,这些问题将得到缓解,正如Tim在下面提到的:

.GetType().GetInterface(typeof(ISurface).FullName)

使用
.FullName
时,名称空间的模糊性也应该没有问题。撇开硬编码不谈,我主要对这种方法本身感兴趣,因为它似乎比通过一系列类型属性检查/LINQ语法更简洁。再说一遍,我不知道引擎盖下到底发生了什么。

如果只通过接口的“简单”名称(而不是完全指定的类名,包括名称空间)进行检查,那么如果在另一个名称空间中有另一个同名的接口,则会出现问题

如果使用完全指定的类名调用
GetInterface
方法,它应该可以正常工作

特别提到,对于泛型类型,应该指定损坏的名称,因此我希望它可以正常工作


当然,如果这是最好的方法,可以辩论。如果更改接口名称,将出现问题。

以下是如何提取所有受支持的
ISurface类型的接口(
.Dump()
扩展方法是LINQPad扩展)。

这是一个可能会更改的实现细节。此外,您还安全地丢失了运行时类型。我宁愿坚持使用公共方法——或者使用您发现的Linq,或者使用反射。

您可能不想这样做的原因有几个:

  • 如果重构代码使
    “ISurface`1”
    不再有效(例如添加或删除类型参数或重命名接口),编译器将无法捕获它。可以用
    typeof(ISurface).Name替换它来解决这个问题
  • 如果在另一个名称空间中有一个
    ISurface
    ,则它是不明确的(或者至少乍一看是这样的)
  • 我可能会和你一起去链接,可能包装在一个扩展方法中,这样我可以更简单地调用它,例如

    public static Type GetInterface(this Type type, Type targetType)
    {
        return type.GetInterfaces().SingleOrDefault(t => t.IsGenericType
                              && t.GetGenericTypeDefinition() == targetType);
    }
    public class Surface : ISurface<int> { /* ... */ }
    
    typeof(Surface).GetInterface(typeof(ISurface<>)); //returns typeof(ISurface<int>)
    typeof(NotASurface).GetInterface(typeof(ISurface<>)); //returns null
    
    公共静态类型GetInterface(此类型,类型targetType)
    {
    返回类型.GetInterfaces().SingleOrDefault(t=>t.IsGenericType
    &&t.GetGenericTypeDefinition()==targetType);
    }
    公共类曲面:ISurface{/*…*/}
    typeof(Surface).GetInterface(typeof(ISurface))//返回类型(ISurface)
    typeof(NotASurface).GetInterface(typeof(ISurface))//返回空值
    
    如果您对所讨论的界面有任何控制权,我建议
    ISurface
    应该继承非通用的
    ISurface
    。该类型又可以包括一个成员,该成员可以以各种方式指示
    ISurface
    将可用的类型(例如,如果定义了一个接口
    iTypeRegistrator{Register(关于类型的信息);}
    ,则非泛型
    ISurface
    可以包括
    注册表支持的类型(iTypeRegistrator)
    方法。根据与每种类型关联的确切信息,典型的实现可能如下所示:

    void RegisterSupportedTypes(ITypeRegistrar registrar)
    {
       registrar.Register<Circle>(circleFactory);
       registrar.Register<Square>(squareFactory);
       registrar.Register<Rhombus>(rhombusFactory);
    }
    
    无效注册表支持的类型(ITypeRegistrator)
    {
    注册处处长(circleFactory);
    注册主任、注册主任(工厂);
    注册处处长(菱形工厂);
    }
    

    客户端代码将接收每个项目作为其实际的泛型类型,因此使用此方法将允许程序在没有类型转换或反射的情况下运行。因为.NET没有开放泛型委托的概念,客户端代码必须手动实现
    ITypeRegistrator
    接口(而不是使用lambda语法作为创建委托的快捷方式)但是实现的方法可以做一些事情,比如在不使用反射的情况下将对提供工厂的引用存储到静态泛型类的字段中,这是委托无法做到的。

    如果您更改接口的名称,或者将第二个名称命名为ISurface,该怎么办?硬编码的类/接口名称是邪恶的!@wudzik-第二个可能是
    ISurface
    ISurface`2
    ,这取决于它所具有的类型参数的数量。但是,硬编码是有问题的。@Bobson是的,会,但我们不能确定:)请参阅更新的问题。如果类型实现了通用接口的两个副本,例如
    ISurface
    ISurface
    @BobVale,那么单一或默认不是问题吗?是的,它会。如果您预期会发生这种情况,那么您需要以某种方式处理它(可能不仅仅是使用
    FirstOrDefault
    !)。是的,我知道这种方法(这与我在问题中链接到的解决方案几乎相同)。我主要感兴趣的是
    GetInterface
    如何堆叠,因为它可以说更短且“更干净”。是的,我知道。更简单的是,非泛型ISurface可以公开类型为的只读属性(例如,
    类型SurfaceType{get;}
    表示派生类的内部类型,可用于进一步的识别和检查。但这不是问题所在。@d7samurai:我不清楚您是否因为喜欢使用反射而想使用反射来构造泛型类型,或者您是否不知道实现pat的任何方法tern不需要它。在某些情况下,代码将希望使用
    类型
    参数调用工厂,但在其他情况下,它将希望使用泛型类型参数。如果普通客户端
    public static Type GetInterface(this Type type, Type targetType)
    {
        return type.GetInterfaces().SingleOrDefault(t => t.IsGenericType
                              && t.GetGenericTypeDefinition() == targetType);
    }
    public class Surface : ISurface<int> { /* ... */ }
    
    typeof(Surface).GetInterface(typeof(ISurface<>)); //returns typeof(ISurface<int>)
    typeof(NotASurface).GetInterface(typeof(ISurface<>)); //returns null
    
    void RegisterSupportedTypes(ITypeRegistrar registrar)
    {
       registrar.Register<Circle>(circleFactory);
       registrar.Register<Square>(squareFactory);
       registrar.Register<Rhombus>(rhombusFactory);
    }