问题:
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
属性。
问题来自于我还想要定义一个StatesChain
,它继承了IState
和IHasStateList
(并且还实现了ITask
和{{ 1}},实现为显式接口)我希望它也隐藏IHasTaskList
的{{1}}并且只显示IHasTaskList
的{{1}}。 (“问题”部分中包含的内容应该在此之后,但我认为首先将它放在读者友好的方式)。
(pff..long text)谢谢!
答案 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;