考虑以下类层次结构:
public interface X { void Foo(); }
public interface Y { void Bar(); }
public class A : X, Y
{
public void Foo() {}
public void Bar() {}
}
public class B : X, Y
{
public void Foo() {}
public void Bar() {}
}
有什么方法可以定义一个列表(或任何通用类型),它可以包含A
&{39}和B
,同时允许我将所述列表的内容视为X
和Y
?即能够让我按照以下方式写一些东西的东西:
var list = ???
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();
修改
为了澄清,我目前正在处理的类型是ObservableCollection<T>
(A
)和ReadOnlyObservableCollection<T>
(B
),其中接口I&#39 ;我感兴趣的是IList<T>
(X
)和INotifyCollectionChanged
(Y
)。很明显,我无法改变他们的班级层次以满足我的需求,所以我需要一个不同的解决方法。
答案 0 :(得分:15)
不,除非您声明另一个界面:
IAB : IA, IB {}
并使两个类都实现它。
您还可以实现自己的集合类,例如List<IFirst, ISecond>
,这样就可以了。
答案 1 :(得分:6)
您可能不想修改A类和B类 所以包装器将完成这项工作
public class XYWrapper : X, Y
{
private dynamic InternalObject { get; set; }
public void Foo() { InternalObject.Foo(); }
public void Bar() { InternalObject.Bar(); }
public static implicit operator XYWrapper(A value)
{
var instance = new XYWrapper();
instance.InternalObject = value;
return instance;
}
public static implicit operator XYWrapper(B value)
{
var instance = new XYWrapper();
instance.InternalObject = value;
return instance;
}
}
所以你以这种方式使用它:
var list = new List<XYWrapper>();
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();
答案 2 :(得分:2)
是的,如果您完全实施explicitly:
public interface IFoo
{
}
public interface IBar
{
}
public class DualList : IList<IFoo>, IList<IBar>
{
List<IFoo> list1 = new List<IFoo>();
List<IBar> list2 = new List<IBar>();
#region IList<IFoo> Members
int IList<IFoo>.IndexOf(IFoo item)
{
return list1.IndexOf(item);
}
// Etc etc
#endregion
#region IList<IBar> Members
int IList<IBar>.IndexOf(IBar item)
{
return list2.IndexOf(item);
}
// Etc etc
#endregion
}
此容器行为充当两个不同类型的独立列表。要使用,您可以这样投射:
((IList<IFoo>)dualList).Add(item);
然而,我质疑这一点的智慧。你肯定无法使用任何已知的序列化程序序列化这样的列表。
答案 3 :(得分:2)
你可以声明,
public class SomeType<T> where T: X, Y
{
public void Do(T t)
{
t.Foo();
t.Bar();
}
}
与here类似,您知道由于通用类型限制,T
必须同时实现X
和Y
。
但是,当您实例化SomeType
时,您需要A
和B
继承或实施的单一类型。
如果你只是添加,
interface Z : X, Y { }
然后更改A
和B
以实施Z
public class A : Z
{
public void Foo() {}
public void Bar() {}
}
public class B : Z
{
public void Foo() {}
public void Bar() {}
}
您只需使用Z
作为通用类型。
答案 4 :(得分:1)
如果您创建两个接口的复合接口:
interface IA
{
void Foo();
}
interface IB
{
void Bar();
}
interface IAB:IA,IB{}
现在开始你的两个课程:
class A:IA,IB
{
public void Foo(){}
public void Bar(){}
}
class B:IA,IB
{
public void Foo(){}
public void Bar(){}
}
并创建实现复合接口的子类(不需要实际实现,因为它已经存在于复合接口中的两个接口中):
class AX:A,IAB
{
}
class BX:B,IAB
{
}
现在你可以......
void Main()
{
List<IAB> list=new List<IAB>();
list.Add(new AX());
list.Add(new BX());
}
答案 5 :(得分:1)
如果要在列表中使用不同的具体类型,每个都实现不同的接口(可能是包含的或互斥的),那么您需要测试每个接口,这通常是使用instance as ISomeInterface
完成并检查是否结果是非空的。
请注意,以下内容适用于实现IFooable和IBarrable的类型,或仅实现两者之一。因此它在保持类型安全的同时为您提供了很多灵活性。唯一的缺点是具体类型可能直接实现标记接口而根本不实现IFooable或IBarrable。
// marker interface
public interface IFooBar
{
}
public interface IFooable:IFooBar
{ void Foo(); }
public interface IBarrable:IFooBar
{ void Bar(); }
public class FooBarProcessor
{
public void ProcessFooBars(IList<IFooBar> foobars)
{
foreach(var foobar in foobars)
{
// feature detection to see which interfaces the instance implements
IBarrable bar = foobar as IBarrable;
if(bar != null)
bar.Bar();
IFooable foo = foobar as IFooable;
if(foo != null)
foo.Foo();
}
}
}
如果您无法控制您的界面,那么您可以使用List<object>
,它的类型安全性稍差,但仍然使用foobar as IBarrable
技术。
如果你仍然想利用标记接口来更多地限制IList,你可以用你喜欢的界面包装你可以改变的界面,其中IOutOfMyControlFooable是你能做的任何界面&#39 ; t改变:
public interface IFooable : IFooBar, IOutOfMyControlFooable
{ void Foo(); }
public interface IBarrable : IFooBar, IOutOfMyControlBarrable
{ void Bar(); }
public class FooBarProcessor
{
public void ProcessFooBars(IList<IFooBar> foobars)
{
foreach(var foobar in foobars)
{
// feature detection to see which interfaces the instance implements
IBarrable bar = foobar as IBarrable;
if(bar != null)
bar.Bar();
IFooable foo = foobar as IFooable;
if(foo != null)
foo.Foo();
}
}
}
答案 6 :(得分:1)
使用这样的包装器类型:
public class Wrapper<T>() {
public WrapperObsCollection(ObservableCollection<T> collection) {
IsReadOnly = false;
ObsCollection = collection;
ROObsCollection = null;
}
public WrapperObsCollection(ReadOnlyObservableCollection<T> collection : base {
IsReadOnly = true;
ObsCollection = null;
ROObsCollection = collection;
}
public bool IsReadOnly { get; private set; }
public ObservableCollection<T> ObsCollection { get; private set; }
public ReadOnlyObsrevableCollection<T> ROObsCollection { get; private set; }
}
- 等。
答案 7 :(得分:0)
你有没有尝试过这样的额外课程......
public class ABList<T> : List<T> where T : X, Y
{
// implement me
}
...并像这样使用它?
var list = new ABList<dynamic>(); // issue here; see comment below
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();
更新:由于编译器问题,解决方案无效(感谢@mikez)。如果类型检查无关紧要,可以优化解决方案,只使用这样的动态列表:
var list = new List<dynamic>();
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();