我通过Facade公开API,返回接口类型的方法,我在C#/ .NET 3.5中遇到了泛型列表和继承的问题。
我有两个接口:
IMyList(实现IList< IMyItem>)
IMyItem
三个班级:
MyList(实现IMyList,扩展List< MyItem>)
MyOtherList(实现IMyList,扩展ObservableCollection< MyItem>)
MyItem(实现IMyItem)
但似乎不可能。我应该如何只暴露必要的东西,但仍然调用方法的正确实现(例如,在MyList和MyOtherList之间可能会有所不同)?
修改
我的门面是一个看起来像这样的工厂:
public static class Facade {
public static IMyList<IMyItem> CreateList() {
return new MyList<MyItem>();
}
public static IMyItem CreateItem() {
return new MyItem();
}
public static IConfiguration CreateConfiguration() {
return new Configuration();
}
}
用法:
var list = Facade.CreateList();
list.DoSomethingOnChildren();
现在我期待在 MyList 中实现的DoSomethingOnChildren()能够在一系列 MyItem 对象上执行。如果我要更改方法返回:
public static IMyList<IMyItem> CreateList() {
return new MyOtherList<MyOtherItem>();
}
我希望在 MyOtherList 中实现的DoSomethingOnChildren()能够在一系列 MyOtherItem 对象上执行。
答案 0 :(得分:1)
仅仅因为MyItem
实施IMyItem
并不意味着MyList<MyItem>
实施IMyList<IMyItem>
。这样说吧,假设你有:
IList<Shape> s = new List<Rectangle>();
如果允许这样做,会导致大量问题,因为它会让你这样做:
s.Add(new Circle());
由于Circle
也是Shape
。一些通用接口支持co / contra-variance,因为泛型类型参数仅以in
或out
方式使用,因为在IList<T>
中,使用T
参数在in
和out
位置,这都很难,而C#也不支持它。
因此,您无法转换引用,但可以通过其他方式(LINQ等)将MyList<MyItem>
的成员加载到IMyList<IMyItem>
并返回列表的新实例。
如果您想支持这两个接口,但也允许特定的实现,您可以使用显式接口实现。
<强>更新强>:
所以,如果你想做类似的事情,你可以这样做。让您的接口返回更通用的实现,然后让您的实现类具有显式实现,以仅返回通用实现,以及特定实现的重载。
类似于:
// purely for illustrative purposes
public interface IShape { }
public class Rectangle : IShape { }
// represents your more "generic" interface
public interface ShapeMaker
{
List<IShape> GetShapes();
}
// Your specific implementation
public class RectangleMaker : ShapeMaker
{
// the explicit implementation of the interface satisfies the
// original, and behaves like original when called from an ShapeMaker
// interface reference
List<IShape> ShapeMaker.GetShapes()
{
return new List<IShape>();
}
// but, we also provide an overload that returns a more specific version
// when used with a reference to our subclass. This gives us more
// functionality.
public List<Rectangle> GetShapes()
{
return new List<Rectangle>();
}
}
您只能通过显式接口实现来执行此操作,因为您仍必须满足原始接口(必须具有相同的返回类型)。但是,它也允许我们说如果从子类引用中使用它,它可以使用更具体的方法。
答案 1 :(得分:0)
写
function IMyList<MyItem> GetList() {
return new MyList<MyItem>();
}
或
function IMyList<IMyItem> GetList() {
return new MyList<IMyItem>();
}
为什么?假设您有两个IMyItem
实现:MyItemA
和MyItemB
。
如果IMyList<IMyItem>
和IMyList<MyItemX>
兼容,则GetList
可以为IMyList<IMyItem>
创建的列表返回new MyList<MyItemA>()
。
function IMyList<IMyItem> GetList() {
return new MyList<MyItemA>();
}
然后你可以做
IMyList<IMyItem> result = GetList();
result[i] = new MyItemB();
但是列表是MyItemA
个对象的列表!因此这是禁止的。
编辑:
请注意,不幸的是,数组允许此
string[] strings = { "a", "b", "c" };
object[] objects = strings;
objects[0] = 5;
这将编译。但是,它将生成此运行时错误:
test.exe中出现未处理的“System.ArrayTypeMismatchException”类型异常
其他信息:尝试将元素作为与数组不兼容的类型进行访问。