为什么类型推断和隐式操作符在以下情况下不起作用

本文关键字:情况下 不起作用 操作符 类型 为什么 | 更新日期: 2023-09-27 17:50:26

我将试着用一个例子来解释我的问题:

class V<T>
{
    public readonly Func<T> Get;
    public readonly bool IsConstant;
    V(Func<T> get, bool isConstant)
    {
        Get = get;
        IsConstant = isConstant;
    }
    public static implicit operator V<T>(T value)
    {
        return new V<T>(() => value, true);
    }
    public static implicit operator V<T>(Func<T> getter)
    {
        return new V<T>(getter, false);
    }
}
void DoSomething<T>(V<T> v)
{
    //...
}
void Main()
{
    DoSomething<string>("test"); // (1) type inference is not working
    DoSomething<string>((V<string>)(() => "test")); // (2) implicit operator does not work
}
在方法Main中,我有两种情况:
  1. 我必须显式地为方法DoSomething指定通用参数<string>
  2. 这里,我必须添加显式强制转换(V<string>),隐式操作符似乎不起作用。

为什么需要这个?编译器正在考虑哪些替代方案,因此无法选择正确的方式?

为什么类型推断和隐式操作符在以下情况下不起作用

您的第二个问题是为什么从()=>""V<string>的隐式转换不成功,即使()=>""可转换为Func<string>, Func<string>可转换为V<string>

同样,我不知道如何回答"为什么不?"的问题,但我知道如何回答"规范的哪一行表明编译器应该拒绝这段代码?"相关行是:

首先,如果需要,执行从源类型到用户定义的或提升的转换操作符的操作数类型的标准转换。

注意这里有一个小错误;这应该说执行从源表达式的标准转换。源表达式可能没有类型。我相信在我离开团队之前,我已经把这个注释给了规范维护者,所以希望这个问题能在下一个版本中得到修复。

无论如何,现在应该清楚这里发生了什么。从lambda到委托类型没有标准的转换,因此用户定义的转换被转换解析算法归类为不适用

我假设你的代码打算调用DoSomething,而不是DumpValue。

你的问题是,首先,为什么

DoSomething("");

不能推断调用的目的是

DoSomething<string>((V<string>)"");

正确吗?

"为什么"的问题很难回答,而"为什么不?"的问题更难回答。相反,我将回答一个可以回答的问题:规范的哪一行证明了这种行为?

重载解析是这样工作的:如果方法组包含泛型方法,但没有提供泛型方法类型参数,则类型推断会尝试推断类型参数。如果不能推断类型参数,则不考虑重载解析该方法。在您的例子中,由于方法组中只有一个方法,删除它将导致重载解析失败。

那么为什么类型推断失败呢?不能推断T,因为规范的控制行是:

如果V是一个构造类型C<V1…Vk>,并且存在一个唯一的类型集合U1…Uk,使得存在从U到C<U1…Uk>的标准隐式转换,则从每个Ui对相应的Vi进行精确推断。

没有标准的隐式转换stringV<string>

是一个用户定义的隐式转换。

因此类型推断失败。

我再回答你的第二个问题。一般来说,同时问两个问题不是个好主意。当你有两个问题时,可以发两个问题

我知道你的问题是"为什么类型推断不起作用",但我只是想在最后解决你关于2种替代方案的陈述。在这种情况下,我认为隐式转换(我讨厌隐式转换)的更好替代方法是静态工厂方法。在我看来,当你调用DumpValue时,语法会更好。

static class VFactory
{
    public static V<T> Create<T>(T value)
    {
        return new V<T>(() => value, true);
    }
    public static V<T> Create<T>(Func<T> getter)
    {
        return new V<T>(getter, false);
    }
}
class V<T>
{
    public readonly Func<T> Get;
    public readonly bool IsConstant;
    internal V(Func<T> get, bool isConstant)
    {
        Get = get;
        IsConstant = isConstant;
    }
}
void DumpValue<T>(V<T> v)
{
    //...
}
void Main()
{
    DumpValue(VFactory.Create("test"));
    DumpValue(VFactory.Create(() => "test"));
}