类层次结构问题(具有泛型差异!)

发布于 2024-08-30 12:47:33 字数 2741 浏览 2 评论 0原文

问题:

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 类?

你会如何处理这种情况?

详细信息: 我定义了一个 ITask 接口:

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

以及一个继承自 ITaskIState 接口:

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 (并且这也实现了 >ITaskIHasTaskList,作为显式接口实现),我希望它也隐藏 IHasTaskListTasks 并仅显示 < code>IHasStateList 的 States(“问题”部分中包含的内容确实应该在这之后,但我认为将其放在前面会更方便读者阅读)。

(pff..长文本) 谢谢!

The problem:

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>.
    }
}

Assuming the IList returned will be read-only, I know that what I'm trying to achieve is safe (or is it not?). Is there any way I can accomplish what I'm trying? I wouldn't want to try to implement myself the TasksChain algorithm (again!), as it would be error prone and would lead to code duplication. Maybe I could just define an abstract Chain and then implement both TasksChain and StatesChain from there? Or maybe implementing a Chain<T> class?

How would you approach this situation?

The Details:
I have defined an ITask interface:

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

and a IState interface that inherits from ITask:

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

I have also defined an IHasTasksList interface:

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

and an IHasStatesList:

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

Now, I have defined a TasksChain, that is a class that has some code logic that will manipulate a chain of tasks (beware that TasksChain is itself a kind of ITask!):

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

    ...

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

    ...
}

I am implementing a State the following way:

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; }
    }
}

which, as you can see, makes use of explicit interface implementations to "hide" FailureTask and instead show FailureState property.

The problem comes from the fact that I also want to define a StatesChain, that inherits both from IState and IHasStateList (and that also imples ITask and IHasTaskList, implemented as explicit interfaces) and I want it to also hide IHasTaskList's Tasks and only show IHasStateList's States. (What is contained in "The problem" section should really be after this, but I thought puting it first would be way more reader friendly).

(pff..long text) Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

有深☉意 2024-09-06 12:47:33

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

不过,您可以返回一个 IEnumerable<>,它在 C# 4 中是协变的。由于这足以使用 LINQ,因此这不应该是太大的缺点,并且表示了可读性:只有自然更好。

In short, no it is not safe, since a "read-only" IList<> doesn't exist (contract-wise). It's only the implementation which will reject entries, but that's too late, as the call itself would require the interface type parameter to be both co- and contravariant at the same time.

You could, however, return an IEnumerable<> instead, which is covariant in C# 4. Since this is enough to use LINQ, that shouldn't be too much of a drawback and expresses the read-only nature better.

掩饰不了的爱 2024-09-06 12:47:33

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

在 C# 3.0 或更早版本中,无法自动实现这一点。 C# 4.0 添加了对协变和逆变的支持,这正是这个目的。但正如您所指出的,这仅在返回的集合为只读时才有效。 IList 类型不能保证这一点,因此它在 .NET 4.0 中不会被注释为协变

要使用 C# 4.0 完成此工作,您需要使用真正的只读类型,它在框架中具有协变注释 - 您的情况的最佳选择是 IEnumerable< /code> (尽管您可以使用 out T 修饰符定义自己的)。

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

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

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

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

On the line where you get an error, you're trying to return IList<IStates> as if it was an instance of type IList<ITask>. This doesn't work automtaically, because the two types are different (no matter that the generic parameters are related).

In C# 3.0 or older, there is no way to achieve that automatically. C# 4.0 adds support for covariance and contravariance, which serves exactly this purpose. But as you noted, this works only when the returned collection is read-only. The IList<T> type doesn't guarantee that, so it isn't annotated as covariant in .NET 4.0.

To make this work using C# 4.0, you'll need to use truly read-only type, which has covariant annotation in the framework - the best option in your case is IEnumerable<T> (although you could define your own using the out T modifier).

To add more details, in C# 4.0, you can declare an interface as either covariant or contra-variant. The first case means that the compiler will allow you to perform the conversion you needed in your example (the other case is useful for write-only classes). This is done by adding explicit annotations to the interface declaration (these are already available for .NET 4.0 types). For example, the declaration of IEnumerable<T> has the out annotation meaning that it supports covariance:

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

Now, the compiler will allow you to write:

IEnumerable<IState> states = ...
IEnumerable<ITask> tasks = states;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文