类层次结构问题(具有通用的差异!)
-
01-10-2019 - |
题
问题:
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
和 IHasTaskList
, ,以显式接口实现),我希望它也隐藏 IHasTaskList
' Tasks
只显示 IHasStateList
' States
. (在此之后,“问题”部分中包含的内容应该是真正的,但是我认为将其放在首位会更加友好)。
(pff ..长文本) 谢谢!
解决方案
在您遇到错误的线上,您正在尝试返回 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;
其他提示
简而言之,不安全,因为“只读” IList<>
不存在(合同)。只是实现将拒绝条目,但这为时已晚,因为呼叫本身将要求接口类型参数同时是共同和违反的。
但是,您可以返回 IEnumerable<>
取而代之的是,在C#4中是协变量的。由于这足以使用LINQ,因此这不应该是一个缺点,并且可以更好地表达仅阅读性质。