【问题标题】:Argument type 'E' is not assignable to parameter type 'TE'参数类型“E”不可分配给参数类型“TE”
【发布时间】:2018-09-29 06:15:29
【问题描述】:

我有一个名为Structure<A, E, B, V, K> 的类,它通过接口继承了一个泛型方法FireEdges<TA, TE, TB, TV, TK>(Func<TE, TK> transformer)

我想根据传递给FireEdges 的类型是否与实例类型匹配来运行不同的代码。

例如,如果我有:

Structure<int, int, int, int, int> s = new ...

如果我打电话给s.FireEdges&lt;int, int, int, int, int&gt;(Func&lt;int, int&gt; transformer) 例如,与调用s.FireEdges&lt;A, B, C, D, E&gt;(Func&lt;B, E&gt; transformer) 相比,它应该运行单独的代码。

为此,我尝试过这样做:

public override void FireEdges<TA, TE, TB, TV, TK>(Func<TE, TK> transformer) {
    if (typeof(TE) == typeof(E) && ... ) //Check other types too
        foreach (E e in Edges)
            e.Target.Data = transformer(e);
    else 
        //Do something else since not all types match
}

我收到此错误:

参数类型“E”不能分配给参数类型“TE”

即使我只是在检查它们确实是同一类型后才尝试分配。

显然我做错了什么,但我不知道会是什么。

谢谢

【问题讨论】:

  • 编译器不知道在这种情况下 Edges 实际上是一个 E,所以它不会全部由你分配。您将需要转换但不能直接执行此操作,请参阅stackoverflow.com/questions/6407039/…
  • 哇,一个有 5 个泛型类型参数的泛型类肯定闻起来很奇怪
  • 我想补充一点,您正在寻找的是其他语言(如 C++)通过模板专业化支持的原生性。

标签: c# generics types


【解决方案1】:

我想根据传递给 FireEdges 的类型是否与实例类型匹配来运行不同的代码。

永远不要这样做。如果您这样做,您的代码不是通用。泛型旨在成为 generic,因此得名。

显然我做错了什么,但我不知道会是什么。

现在你知道了。 如果您对泛型进行类型测试,则几乎可以肯定您做错了什么

正如其他答案所指出的那样,如果您一心想以这种方式滥用泛型,您可以引入对对象的强制转换,然后对“出站”类型进行强制转换以绕过错误。 这可能会引入装箱惩罚,因为历史上抖动没有优化 T -> 装箱 T -> T 代码路径。 在这种情况下,可能已经修复了抖动以生成最佳代码,但我会检查一下。

【讨论】:

    【解决方案2】:

    仅适用于少数类型的泛型并不是真正的泛型,它只是将自己隐藏在泛型毛皮中的某种特定类型。因此,如果您的 TE-argumen 仅适用于 E 类型的实例,则不应使用泛型。而是使用可能具有该类型的普通方法,例如ReadIntParseString 或其他任何东西,

    除此之外,在编译时没有任何东西可以将E 限制为TE 类型,只有在运行时 进行类型检查。这让编译器无法知道如何从E 生成TE。您必须使用演员表提供该信息。但是,由于这些类型之间没有关系,特别是 TE 甚至可能是一个值类型,您可能必须先转换为 object

    var te = (TE)(object)e;
    

    【讨论】:

    • jit 可能不会优化拳击操作的引入——上次我问 jitter 团队时,它没有,但那是十年前的事了,所以事情可能有改变了。无论如何,我希望这种解决方案会破坏使用泛型所带来的性能提升。
    • 顺便说一句,他们本周末将在沃尔玛打折普通毛皮。
    【解决方案3】:

    问题是只有你知道你的参数是TE类型,但你的编译器不知道。你只需要一个演员:

    e.Target.Data = transformer((TE)(object)e);
    

    【讨论】:

    • 也没有从 E 到 TE 的直接转换。你需要类似transformer((TE)(object)e);
    • @mike z,对不起,我问了,但你怎么知道?
    • 他可以通过编译代码并从编译器中获取错误来了解它。如果它们不受约束,C# 不允许您直接从一个类型参数转换为另一个类型参数;你能猜出为什么要实施这个限制吗?
    • 您的选择是 (1) 实现该功能,以便每次转换在运行时再次启动编译器,或者 (2) 使其成为错误并进行简单转换知道要安全非常快。 C#采取了第三条路线:实现dynamic,这样如果你想付出(1)的代价就可以,否则,我们选择(2)。
    • 哇,我没想到会这样,非常感谢@Eric Lippert 的澄清!
    猜你喜欢
    • 1970-01-01
    • 2019-06-24
    • 2021-06-14
    • 2018-08-10
    • 2021-10-17
    • 1970-01-01
    • 2021-02-03
    • 2020-03-26
    相关资源
    最近更新 更多