2

问题:

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算法(再次!),因为它容易出错并且会导致代码重复。也许我可以定义一个抽象链,然后从那里实现TasksChain两者StatesChain?或者也许实现一个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属性。

问题来自这样一个事实,即我还想定义 a StatesChain,它继承自IStateand IHasStateList(并且它也实现ITaskand IHasTaskList,实现为显式接口)并且我希望它也隐藏IHasTaskList'sTasks并且只显示IHasStateList's States(“问题”部分中包含的内容应该在此之后,但我认为把它放在第一位会更便于读者阅读)。

(pff..长文本)谢谢!

4

2 回答 2

2

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

但是,您可以返回一个IEnumerable<>代替,它在 C# 4 中是协变的。由于这足以使用 LINQ,因此这不应该是太大的缺点,并且可以更好地表达只读性质。

于 2010-04-26T23:38:22.477 回答
1

在出现错误的那一行,您正试图返回IList<IStates>,就好像它是 type 的实例一样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;
于 2010-04-26T23:39:07.973 回答