【问题标题】:Working with non generic parts of a generic interface使用通用接口的非通用部分
【发布时间】:2016-10-06 10:32:11
【问题描述】:

考虑以下代码:

public interface IStuff<T>
{
    IList<T> Items { get; set; }
    bool IsExpanded { get; set; }
}

public interface IBar
{
    string Name { get; set; }
}

public interface IFoo : IBar, IStuff<IBar>
{
}

public class MyClass : IFoo 
{
    public Name {get;set;
    public IList<IBar> Items { get; set;}
    bool IsExpanded { get; set; }
}

当我使用它时,我实际上要做的是用IFoo 的实例填充MyClass.Items,这是允许的,因为IFoo 实现了IBar,所以一切都很好。

但是,我在控件中使用了一种方法,该方法不知道也不应该知道IBar。只需要知道IStuff&lt;&gt;具体有一个列表和一个属性。

当我使用Items 时,我将检测该项目是否像这样实现IStuff

public void ProcessItems( object item )
{
    IStuff<object> stuff = item as IStuff<object>;
    stuff.IsExpanded = true;
    foreach( var childItem in stuff.items )
    {
        ProcessItems( childItem );
    }
}

这里的问题是,转换为IStuff&lt;object&gt; 现在不起作用,它返回 null。 我想做的只是使用接口的通用部分,我不在乎T 是什么。

问题是如何替换

IStuff<object> stuff = item as IStuff<object>;

以便我可以使用IStuff&lt;&gt; 接口中声明的项目?

【问题讨论】:

  • 也许你应该把你的接口分成两个,一个是通用版本,一个是非通用版本,其中通用版本继承自非通用版本。然后你可以在不知道&lt;T&gt; 的情况下使用这些对象,但当然你只能访问不需要T 的东西。
  • @HimBromBeere 为您解决了这个问题。

标签: c# generics reflection interface


【解决方案1】:

听起来您需要在您的界面IStuff 上使用co-variance。但是,由于 IList&lt;T&gt; 不是协变的,您可以使用支持协变的接口,例如 IEnumerable&lt;T&gt;,它 自 .NET 4.0 起是协变的:

public interface IStuff<out T>
{
    IEnumerable<T> Items { get; set; }
    bool IsExpanded { get; set; }
}

现在您可以将IStuff&lt;IBar&gt; 的实例强制转换为IStuff&lt;object&gt;

【讨论】:

  • 这很有希望,目前界面看起来还可以,但是我无法更改 MyClass 来声明 IEnumerable。它必须是一个列表。这是因为还有另一个我没有提到的实现需要一个列表。我还需要能够添加项目等等。
  • 但是你可以在类上实现该接口并重定向到MyClass.Items,所以你有IStuff.Items,它返回一个IEnumerable&lt;T&gt;,需要明确声明,MyClass.Items,它返回一个@ 987654332@。这不会改变MyClass 的API,它会通过改变的接口来增强它。
  • 我还在看这个,看起来很有希望。只是有很多其他代码正在实现 ICollection,我需要修改这些代码才能编译。我也喜欢这个集合最终会被包裹在课堂上。
  • 也许您可以创建一个支持IEnumerable 的新接口,并在您的类中实现IFoo-接口和新接口。当然,类上新接口的实现只是简单地重定向到已经存在的方法。
  • 使用 IStuff 效果很好。我现在已经处理了这些项目,因此应该可以使用 IsExpanded 和项目以及我将添加到界面中的其他内容。我喜欢这个解决方案也不需要反射。
【解决方案2】:

您能否将IStuff 拆分为非泛型和泛型部分(继承自非泛型部分),然后只处理代码中的非泛型部分?例如

public interface IStuff
{
    IsExpanded { get; set; }
}
public interface IStuff<T> : IStuff
{
    IList<T> Items { get; set; }
}

【讨论】:

  • 不幸的是我不能这样做。我想要做的是使用 IStuff 在一个树视图的控件中工作,项目将成为链接到孩子的部分。
【解决方案3】:

如果您不介意反射,您可以阅读该属性并将其转换为IEnumerable

var listType = item.GetType().GetProperty("Items");
var enumerable = (IEnumerable)(listType.GetValue(item));

同样你可以设置扩展标志。

【讨论】:

  • 问题在于将集合 within 的实例转换为通用接口 IStuff
  • @HimBromBeere 但他问如何替换演员表以处理列表中的对象。但我也很好奇更好的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-07
  • 1970-01-01
  • 1970-01-01
  • 2023-01-28
相关资源
最近更新 更多