【问题标题】:How to return a generic when the actual generic returned type is a sub class of the methods defined return type? [duplicate]当实际的泛型返回类型是方法定义的返回类型的子类时,如何返回泛型? [复制]
【发布时间】:2021-08-23 07:52:06
【问题描述】:

考虑代码:

public Task<IHttpActionResult> GetAliases()
{
    OkNegotiatedContentResult<IEnumerable<Stuff>> okNegotiatedContentResult 
        = Ok(GET Enumerable "STUFF");

    //wrap stuff in task
    Task<OkNegotiatedContentResult<IEnumerable<Alias>>> fromResult 
        = Task.FromResult(okNegotiatedContentResult);

    // return result
    return fromResult;
}

我得到错误:

无法将表达式类型 'System.Threading.Tasks.Task>>' 转换为返回类型'System.Threading.Tasks.Task'

但是OkNegotiatedContentResult 是 IHttpActionResult 的子类,它是返回类型中指定的泛型...

那么为什么这不起作用?以及如何让它工作?是否有某种方法可以在返回类型上表明泛型类的子类也可以接受?

【问题讨论】:

  • 对于“为什么”,请参阅this。为什么不直接做var fromResult = Task.FromResult&lt;IHttpActionResult&gt;(okNegotiatedContentResult);
  • 击败我..
  • 另外,如果你使用 async 关键字,这不会是一个问题,因为编译器会处理它
  • @Sweeper “为什么”问题的链接答案在这里不适用。正如 Eric 在 his answer 中对另一个链接问题所说:Task&lt;T&gt; 是那些可以安全协变的稀有类之一”
  • @41686d6564:“可以”与“是”不同。您链接的答案仍然集中在 Task&lt;T&gt; 就语言而言不是规则的例外这一事实。

标签: c# generics


【解决方案1】:

但是 OkNegotiatedContentResult 是 IHttpActionResult 的子类,它是返回类型中指定的泛型...

您遇到了协变和逆变的问题。仅仅因为A 可以向下转换为B不会使C&lt;A&gt; 可以向下转换为C&lt;B&gt;

一个简单的例子来证明这一点:

public class Animal {}

public class Dog : Animal {}
public class Cat : Animal {}

private List<Animal> GetList() { /* hidden */ }

List<Animal> list = GetList();

list.Add(new Dog());

完全有效的代码,对吧?好吧,如果隐藏的方法逻辑被揭露出来怎么办:

private List<Animal> GetList() { return new List<Cat>(); }

突然间,您将在List&lt;Cat&gt; 中添加Dog。这是不对的!

这就是为什么您不能像返回 List&lt;Animal&gt; 一样返回 List&lt;Cat&gt;。而且,对于您的情况,为什么您不能像返回 Task&lt;IHttpActionResult&gt; 一样返回 Task&lt;OkNegotiatedContentResult&lt;IEnumerable&lt;Alias&gt;&gt;&gt;

这里的解决方案是确保泛型类型已经设置为基类。对于Task.FromResult,这实际上是一个泛型方法,默认情况下通过传递参数的类型推断泛型类型。

您可以更改传递参数的类型:

Task<IHttpActionResult> fromResult 
    = Task.FromResult(okNegotiatedContentResult as IHttpActionResult);

或者您可以明确说明泛型参数应该是什么:

Task<IHttpActionResult> fromResult 
    = Task.FromResult<IHttpActionResult>(okNegotiatedContentResult);

两者都是解决手头问题的方法。

【讨论】:

  • 再一次,你不能用why it couldn't work for List&lt;T&gt;的理由来回答why it doesn't work for Task&lt;T&gt;。它们不一样。
  • @41686d6564:如果语言不能区分这两种情况,这里没有,那么可以。 List&lt;T&gt; 的协方差问题正是语言天生拒绝这种模式的原因,因此也拒绝 Task&lt;T&gt; 的相同模式。
  • @41686d6564:您自己的链接明确指出 C# 不允许在类上出现差异。该语言对 which 类的使用没有区别。语言在实现差异保护方面是否过分热心与为什么首先需要这种保护无关。这两件事都值得一提,但前者并不会使后者无效。
  • @41686d6564:换句话说,人们不再在工作中获得免费咖啡的原因是“因为 Bob 用免费的咖啡卖给路人”。即使你没有向路人卖咖啡,你也不能再得到免费的咖啡;这不会改变制定规则的原因
  • “C# 不允许对类进行方差” 是一个有效的答案,但关于List&lt;T&gt; 的论点 在这里适用。也许您的答案可以改写以解释List&lt;T&gt; 示例只是语言设计者做出该决定的原因之一。到目前为止,您似乎对 Task&lt;T&gt; 使用了相同的参数,这实际上不起作用。
猜你喜欢
  • 1970-01-01
  • 2016-12-24
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多