考虑以下代码:
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<>
,特别是有一个列表和一个属性。
当我使用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<object>
现在不工作,它返回null。我想做的是只使用接口的通用部分,我不关心T
是什么。
问题是如何替换
IStuff<object> stuff = item as IStuff<object>;
以便我可以使用在IStuff<>
接口中声明的项?
听起来你需要在你的接口IStuff
上进行协方差。但是,由于IList<T>
不是协变的,您可以使用支持协变的接口,例如IEnumerable<T>
,其中自。net 4.0以来是协变的:
public interface IStuff<out T>
{
IEnumerable<T> Items { get; set; }
bool IsExpanded { get; set; }
}
现在可以将IStuff<IBar>
的实例强制转换为IStuff<object>
。
您可以将IStuff
拆分为非泛型和泛型部分(从非泛型部分继承),然后仅处理代码中的非泛型部分吗?例如
public interface IStuff
{
IsExpanded { get; set; }
}
public interface IStuff<T> : IStuff
{
IList<T> Items { get; set; }
}
如果您不介意反射,您可以读取属性并将其强制转换为IEnumerable
:
var listType = item.GetType().GetProperty("Items");
var enumerable = (IEnumerable)(listType.GetValue(item));
同样,您可以设置扩展标志