【问题标题】:How do I enumerate all items that implement a generic interface?如何枚举实现通用接口的所有项目?
【发布时间】:2023-03-26 14:59:01
【问题描述】:

我有两个接口,一个泛型和一个非泛型,它们具有继承层次结构:

public interface IGenericRelation<TParent, TChild> : IRelation

public interface IRelation

通用的由几个动态加载的服务器控件实现,我希望枚举实现此接口的控件集合。我可以做到以下几点

    foreach (IRelation relationControl in this.uiPlhControls.Controls.OfType<IRelation)
    { ... }

但我真正想做的是……

    foreach (IGenericRelation<,> relationControl in this.uiPlhControls.Controls.OfType<IGenericRelation<,>)
    { ... }

然后能够将relationControl 与它提供的类型一起使用,因为这样我就可以访问IGenericRelation 上可用的强类型属性。不幸的是,这是不可能的,因为我似乎不能省略类型参数。

有谁知道一种方法来枚举实现通用接口的控件以防止我不得不编写多个循环而不是一个循环?也许使用反射?

【问题讨论】:

  • 我似乎记得一个早期的 Linq 样本正是这样做的,但是当我来查看 linq 时却找不到它。我在看这个空间。 . .

标签: c# generics reflection collections


【解决方案1】:

这是不可能的,因为IGenericRelation&lt;T,F&gt; 是与IGenericRelation&lt;G,I&gt; 完全不同的类型。如果您需要访问所有IGenericRelation 共有的特定属性,那么您需要在IRelation 层实现它们,或者在IRelationIGenericRelation&lt;,&gt; 之间引入第三个接口来实现这些.这样做的原因是编译器无法推断期望它实现什么类型。

解决此问题的最简单方法是将您的两个属性实现为更高级别的objectIRelation 或中间接口)并在IGenericRelation&lt;,&gt; 级别进行强类型化。

【讨论】:

    【解决方案2】:

    您要访问哪些强类型属性?如果它们是强类型的,因为它们是泛型的输入类型,那么如果不提供 foreach 循环中的类型,您将无法访问它们。如果它们是强类型的,但与提供的类型无关,你能把它们移到 IRelation 类吗?

    使用代码示例会更有意义 - 假设您的类类似于:

    public IRelation
    {
       public string RelationshipType { get; set; }
    }
    
    public IGenericRelation<TParent, TChild> : IRelation
    {
        public TParent Parent { get; set; }
        public TChild Child { get; set; }
    }
    

    如果您的列表包含一个IGenericRelation&lt;Foo, Bar&gt; 和一个IGenericRelation&lt;Fizz, Buzz&gt;,则在不知道您要查找的具体类型的情况下,您无法枚举并返回两者:

    //Theoretical, non-compiling example....
    foreach (IGenericRelation<,> relationControl in this.uiPlhControls.Controls.OfType<IGenericRelation<,>>)
    { 
        //This wouldn't work for type IGenericRelation<Fizz, Buzz>
        relationControl.Parent.FooProperty = "Wibble";
    
        //You would be able to access this, but there is no advantage over using IRelation
        relationControl.RelationshipType = "Wibble";
    }
    

    (请注意,我还必须从您的示例代码中更改 foreach 中的 relationControl 类型,因此可能的用法是有意义的。)


    从根本上说,将 .NET 泛型视为与 C++ 模板类相同会有所帮助(我知道实现不同,但在这方面的效果是相同的)。想象一下,在编译时检查所有代码是否使用 IGenericRelation 类,并通过查找 TParent 和 TChild 关键字并将它们替换为请求的类型来创建具体的非泛型类。由于这两个创建的类与其他任何两个 .NET 类一样独立,因此请求“所有以该模板开始的类”是没有意义的,您能做的最好的事情就是寻找一个共享的基类或接口——在这种情况下关系。

    【讨论】:

    • 是的,它们是泛型接口的输入类型。
    猜你喜欢
    • 2010-11-18
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-03
    • 2020-11-28
    相关资源
    最近更新 更多