如何以多态方式对待 List<Task<T>> 以处理异常
How to treat List<Task<T>> polymorphically to handle exceptions
我正在尝试创建一种方法来记录任务列表的内部异常。问题是在某些地方我只有 (A) List<Task>
而在其他地方我有 (B) List<Task<Something>>
。我试图创建一个接受 A 的方法,但编译器不接受使用 B 对该方法的调用,因此我将 B 称为“MultipleTasksExceptionHandler(B as A)”,但在该方法中我收到 null。所以我不得不创建几乎相同的方法,如下所示,它们可以工作,但是有没有其他方法可以避免这种代码重复?
public static void MultipleTasksExceptionHandler<T>(IList<Task<T>> tasks, ILogger logger)
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
public static void MultipleTasksExceptionHandler(IList<Task> tasks, ILogger logger)
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
使用协变 collection,例如 IEnumerable<T>
或 IReadOnlyList<T>
,而不是 IList<T>
:
public void MultipleTasksExceptionHandler(IEnumerable<Task> tasks, ILogger logger)
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
您不能将 IList<Task<T>>
类型的 object 分配给 IList<Task>
类型的变量,这是正确的。如果这被允许,你将能够做 list.Add(Task.CompletedTask)
,并在你的 IList<Task<T>>
中插入一个 non-generic Task
,这显然是错误的。
例如:
IList<Task<int>> listOfTaskOfT = new List<Task<int>>();
// This isn't allowed
IList<Task> listOfTask = listOfTaskOfT;
// ... if it was, you'd be able to do this:
listOfTask.Add(new Task());
Task<int> task = listOfTaskOfT[0];
// But that item is a Task, not a Task<int>. Oops!
但是,如果您只能从 collection 中取出 的东西,这不是问题。这叫generic covariance, and generic interfaces can mark themselves as covariant using the out
keyword. IEnumerable<out T>
and IReadOnlyList<out T>
都这样做。
然后您可以执行以下操作:
IEnumerable<Task> tasks = new List<Task<T>>();
...前提是涉及的所有泛型类型参数都是引用类型。
(通用“逆变”界面存在类似的功能,您只能使用 in
关键字添加项目)。
您可以使用泛型获得类似的功能:
public void MultipleTasksExceptionHandler<T>(IList<T> tasks, ILogger logger) where T : Task
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
在这里,由于 T
是 Task<Whatever>
,您无法将 Task
添加到 IList<Task<Whatever>>
中。这在您的情况下不是必需的,但如果您确实需要将项目插入到 collection.
中,这可能会很有用
您只会迭代任务,绝不会以任何其他方式操纵集合。如果您实际上不需要它的功能,则不应接受 IList
。相反,接受 IEnumerable<Task>
,因为它是协变的,所以你的问题就完全消失了。
我正在尝试创建一种方法来记录任务列表的内部异常。问题是在某些地方我只有 (A) List<Task>
而在其他地方我有 (B) List<Task<Something>>
。我试图创建一个接受 A 的方法,但编译器不接受使用 B 对该方法的调用,因此我将 B 称为“MultipleTasksExceptionHandler(B as A)”,但在该方法中我收到 null。所以我不得不创建几乎相同的方法,如下所示,它们可以工作,但是有没有其他方法可以避免这种代码重复?
public static void MultipleTasksExceptionHandler<T>(IList<Task<T>> tasks, ILogger logger)
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
public static void MultipleTasksExceptionHandler(IList<Task> tasks, ILogger logger)
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
使用协变 collection,例如 IEnumerable<T>
或 IReadOnlyList<T>
,而不是 IList<T>
:
public void MultipleTasksExceptionHandler(IEnumerable<Task> tasks, ILogger logger)
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
您不能将 IList<Task<T>>
类型的 object 分配给 IList<Task>
类型的变量,这是正确的。如果这被允许,你将能够做 list.Add(Task.CompletedTask)
,并在你的 IList<Task<T>>
中插入一个 non-generic Task
,这显然是错误的。
例如:
IList<Task<int>> listOfTaskOfT = new List<Task<int>>();
// This isn't allowed
IList<Task> listOfTask = listOfTaskOfT;
// ... if it was, you'd be able to do this:
listOfTask.Add(new Task());
Task<int> task = listOfTaskOfT[0];
// But that item is a Task, not a Task<int>. Oops!
但是,如果您只能从 collection 中取出 的东西,这不是问题。这叫generic covariance, and generic interfaces can mark themselves as covariant using the out
keyword. IEnumerable<out T>
and IReadOnlyList<out T>
都这样做。
然后您可以执行以下操作:
IEnumerable<Task> tasks = new List<Task<T>>();
...前提是涉及的所有泛型类型参数都是引用类型。
(通用“逆变”界面存在类似的功能,您只能使用 in
关键字添加项目)。
您可以使用泛型获得类似的功能:
public void MultipleTasksExceptionHandler<T>(IList<T> tasks, ILogger logger) where T : Task
{
var exceptions = tasks.Where(task => task.IsFaulted).SelectMany(x => x.Exception.InnerExceptions);
foreach (var exception in exceptions)
{
logger.LogError(exception, exception.Message);
}
}
在这里,由于 T
是 Task<Whatever>
,您无法将 Task
添加到 IList<Task<Whatever>>
中。这在您的情况下不是必需的,但如果您确实需要将项目插入到 collection.
您只会迭代任务,绝不会以任何其他方式操纵集合。如果您实际上不需要它的功能,则不应接受 IList
。相反,接受 IEnumerable<Task>
,因为它是协变的,所以你的问题就完全消失了。