类层次结构问题(使用泛型方差!)

时间:2010-04-26 23:31:06

标签: c# generics oop generic-variance

问题:

class StatesChain : IState, IHasStateList {
    private TasksChain tasks = new TasksChain();

     ...

    public IList<IState> States {
        get { return _taskChain.Tasks; }
    }

    IList<ITask> IHasTasksCollection.Tasks {
        get { return _taskChain.Tasks; } <-- ERROR! You can't do this in C#!
                                             I want to return an IList<ITask> from
                                             an IList<IStates>.
    }
}

假设返回的IList是只读的,我知道我想要实现的是安全的(或者不是吗?)。有什么方法可以完成我正在尝试的东西吗?我不想尝试自己实现TasksChain算法(再次!),因为它容易出错并导致代码重复。也许我可以定义一个抽象链,然后从那里实现TasksChainStatesChain?或者可能实现Chain<T>类?

你会如何处理这种情况?

详细信息: 我已经定义了ITask接口:

public interface ITask {
    bool Run();
    ITask FailureTask { get; }
}

IState接口继承自ITask

public interface IState : ITask {
    IState FailureState { get; }
}

我还定义了IHasTasksList界面:

interface IHasTasksList {
    List<Tasks> Tasks { get; }
}

IHasStatesList

interface IHasTasksList {
    List<Tasks> States { get; }
}

现在,我已经定义了一个TasksChain,这个类有一些代码逻辑可以操作一系列任务(请注意TasksChain本身就是一种ITask! ):

class TasksChain : ITask, IHasTasksList {
    IList<ITask> tasks = new List<ITask>();

    ...

    public List<ITask> Tasks { get { return _tasks; } }

    ...
}

我正在通过以下方式实施State

public class State : IState {
    private readonly TaskChain _taskChain = new TaskChain();

    public State(Precondition precondition, Execution execution) {
        _taskChain.Tasks.Add(precondition);
        _taskChain.Tasks.Add(execution);
    }

    public bool Run() {
        return _taskChain.Run();
    }

    public IState FailureState {
        get { return (IState)_taskChain.Tasks[0].FailureTask; }
    }

    ITask ITask.FailureTask {
        get { return FailureState; }
    }
}

正如您所看到的,它使用显式接口实现来“隐藏”FailureTask,而是显示FailureState属性。

问题来自于我还想要定义一个StatesChain,它继承了IStateIHasStateList(并且还实现了ITask和{{ 1}},实现为显式接口)我希望它也隐藏IHasTaskList的{​​{1}}并且只显示IHasTaskList的{​​{1}}。 (“问题”部分中包含的内容应该在此之后,但我认为首先将它放在读者友好的方式)。

(pff..long text)谢谢!

2 个答案:

答案 0 :(得分:2)

简而言之,不是不安全,因为“只读”IList<>不存在(合同方式)。它只是拒绝条目的实现,但为时已晚,因为调用本身需要接口类型参数同时具有共变和逆变。

然而,您可以返回IEnumerable<>,这在C#4中是协变的。因为这足以使用LINQ,这不应该是一个缺点并且表达更好的只读性质

答案 1 :(得分:1)

在您收到错误的行上,您尝试返回IList<IStates>,就像它是IList<ITask>类型的实例一样。这不能自动工作,因为这两种类型是不同的(无论通用参数是否相关)。

在C#3.0或更早版本中,没有办法自动实现。 C#4.0增加了对协方差和逆变的支持,正是出于这个目的。但正如您所指出的,这仅在返回的集合是只读时才有效。 IList<T>类型不保证,因此在.NET 4.0中没有注释为协变

要使用C#4.0进行此项工作,您需要使用真正只读类型,它在框架中具有协变注释 - 在您的案例中最好的选项是IEnumerable<T> (尽管您可以使用out T修饰符定义自己的。)

要在C#4.0中添加更多详细信息,您可以将接口声明为协变或反变量。第一种情况意味着编译器将允许您执行示例中所需的转换(另一种情况对于只写类很有用)。这是通过向接口声明添加显式注释来实现的(这些注释已经可用于.NET 4.0类型)。例如,IEnumerable<T>的声明具有out注释,意味着它支持协方差:

public interface IEnumerable<out T> : IEnumerable { /* ... */ }

现在,编译器将允许您编写:

IEnumerable<IState> states = ...
IEnumerable<ITask> tasks = states;