C#规范是否禁止基于默认参数进行类型推断?

问题描述

在下面的代码中,我试图以工厂模式完成认类型推断。我想使用参数的任意组合来调用工厂方法,并从省略的参数的认参数中推断出泛型类型参数。但是,这种尝试将导致错误CS1750。 CS1750 is mentioned in a handful of places in the roslyn repo,但在这种情况下,对其进行审查并不能充分了解原因。

错误的全文如下:

(参数)S a =新S()

“ S”类型的值不能用作认参数,因为没有标准转换为“ S”类型[TypeInference] csharp(CS1750)

public class T {}
public class T0  : T {}
public class T1  : T {}
// T2{} .. T9{}
public class T10 : T {}
public struct S<T> {}
public class X {} // X is a complex object strongly-typed with T1..T10
public static class C {
    public static X Factory<A,B/*,C..J*/>(
        S<T0> s = new S<T0>(),// this is fine
        S<A>  a = new S<T0>(),// ERROR: no standard conversions to type...
        S<B>  b = new S<T0>()  // ...'S<A>' [TypeInference]csharp(CS1750)
        //S<C> c,//...
        //S<J> j
    )
    where A : T
    where B : T
    =>throw new System.NotImplementedException();

    static void usage1() {
        /* I'd like to be able to omit an arbitrary portion
           of the factory arguments and have type inference use
           the default values.*/
        Factory(b : new S<T1>());
    }

    // simplified example
    public static void Foo<A>(A a)    {}
    public static void Bar<A>(A a=42) {} // ERROR: CS1750

    static void usage2() {
        Foo(42); // this is fine
    }
}

我一直试图理解为什么提出CS1750。根据{{​​3}}

15.6.2方法参数...

认参数中的表达式应为以下之一: ...

  • 形式为new S()的表达式,其中S是值类型

new S<T0>()似乎符合此条件。实际上,参数声明S<T0> s = new S<T0>()似乎没有引发错误

读取(当然不完全吸收)规范的类型推论部分,为什么在类型推论期间不考虑认值并不清楚。该部分中的语言甚至似乎在区分带有或不带有相应参数的可选参数时都非常谨慎。例如,在这句话中,排除了缺少的可选参数作为推断失败的原因:

12.6.3类型推断...

如果...存在一个非可选参数且没有相应的参数,则推理将立即失败。

与其排除认参数,倒不如说是一个很弱的建议,即类型推断可以基于它们。

  1. 为什么编译器尝试将S<T0>转换为S<A>而不是推断AT0
  2. 规范是否禁止基于认参数的类型推断?

解决方法

我认为,问题1的答案是“编译器无法那样工作”。就像我在评论中所做的那样,编译器期望程序员根据当前的类型约束为默认参数提供有效的类型。编译器不会根据默认参数的类型为您推断通用参数。在这里,唯一的约束是A和T0都从T下降,因此通常我们不能期望S<A> a = new S<T0>()起作用。我很确定这就是错误的意思。

我认为您的问题2确实是“可以那样工作吗?”。我认为理论上可以,但是存在一些问题。考虑一下,在您的示例中,如果有人进行呼叫C.Factory<T1,T1>(),从而在不提供任何参数的情况下将A显式设置为T1类型,会发生什么情况。现在,当我们尝试将new S<T0>()分配给类型S<A> = S<T1>的参数a时,出现了一个错误情况,因为S<T0>无法分配给{{1} }。编译器应如何处理?由于A仅被约束为T的后代,并且Factory具有不带任何参数的重载,因此看起来该调用是有效的。因此,任何例外充其量只能使人迷惑,甚至可以说是错误的。但是我们不想在程序员明确要求其他类型之后就将A推定为T0。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...