是否可以定义实现多个接口的任何类型的列表?
Is it possible to define a list of any type that implements multiple interfaces?
考虑以下 class 层次结构:
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
和 B
,同时允许我处理内容所述列表的 X
和 Y
? IE。可以让我按照以下方式写一些东西的东西:
var list = ???
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();
编辑
澄清一下,我目前处理的类型是 ObservableCollection<T>
(A
) and ReadOnlyObservableCollection<T>
(B
),我感兴趣的接口是 IList<T>
(X
) 和 INotifyCollectionChanged
(Y
)。显然,我无法更改他们的 class 层次结构来满足我的需求,因此我需要一个不同的解决方法。
不,除非你声明另一个接口:
IAB : IA, IB {}
并让两个 class 都实现它。
您也可以实现自己的集合 class,类似于 List<IFirst, ISecond>
,这将允许这样做。
是的,如果你完全实施它们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);
然而,我质疑这是否明智。使用任何已知的序列化器序列化这样的列表肯定会遇到问题。
你试过这样的额外class吗...
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();
如果您创建两个接口的复合接口:
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());
}
如果你要在一个列表中有不同的具体类型,每个都实现一个不同的接口(可能包含或互斥),那么你需要测试每个接口,这通常使用 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 是您不能更改的任何接口变化:
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();
}
}
}
您可能不想修改 class 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();
使用这样的包装器类型:
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; }
}
--等等
可以申报,
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
作为通用类型。
考虑以下 class 层次结构:
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
和 B
,同时允许我处理内容所述列表的 X
和 Y
? IE。可以让我按照以下方式写一些东西的东西:
var list = ???
list.Add(new A());
list.Add(new B());
list.First().Foo();
list.Last().Bar();
编辑
澄清一下,我目前处理的类型是 ObservableCollection<T>
(A
) and ReadOnlyObservableCollection<T>
(B
),我感兴趣的接口是 IList<T>
(X
) 和 INotifyCollectionChanged
(Y
)。显然,我无法更改他们的 class 层次结构来满足我的需求,因此我需要一个不同的解决方法。
不,除非你声明另一个接口:
IAB : IA, IB {}
并让两个 class 都实现它。
您也可以实现自己的集合 class,类似于 List<IFirst, ISecond>
,这将允许这样做。
是的,如果你完全实施它们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);
然而,我质疑这是否明智。使用任何已知的序列化器序列化这样的列表肯定会遇到问题。
你试过这样的额外class吗...
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();
如果您创建两个接口的复合接口:
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());
}
如果你要在一个列表中有不同的具体类型,每个都实现一个不同的接口(可能包含或互斥),那么你需要测试每个接口,这通常使用 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 是您不能更改的任何接口变化:
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();
}
}
}
您可能不想修改 class 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();
使用这样的包装器类型:
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; }
}
--等等
可以申报,
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
作为通用类型。