C#/。NET 3.5:转换扩展通用列表的对象

时间:2012-01-26 17:50:21

标签: c# generics inheritance .net-3.5 polymorphism

我通过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 对象上执行。

2 个答案:

答案 0 :(得分:1)

仅仅因为MyItem实施IMyItem并不意味着MyList<MyItem>实施IMyList<IMyItem>。这样说吧,假设你有:

IList<Shape> s = new List<Rectangle>();

如果允许这样做,会导致大量问题,因为它会让你这样做:

s.Add(new Circle());

由于Circle也是Shape。一些通用接口支持co / contra-variance,因为泛型类型参数仅以inout方式使用,因为在IList<T>中,使用T参数在inout位置,这都很难,而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实现:MyItemAMyItemB

如果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”类型异常

其他信息:尝试将元素作为与数组不兼容的类型进行访问。