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

时间:2013-01-01 23:04:37

标签: c# .net generics type-inference implicit-conversion

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

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. 我必须为方法<string>明确指定通用参数DoSomething
  2. 在这里,我必须添加显式转换(V<string>),隐式运算符似乎不起作用。
  3. 为什么需要这个?编译器正在考虑哪些替代方案,因此无法选择正确的方法?

3 个答案:

答案 0 :(得分:5)

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

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

  

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

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

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

答案 1 :(得分:3)

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

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

DoSomething("");

没有推断该电话是

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

正确?

“为什么”这些问题很难回答,“为什么不呢?”问题更难。相反,我会回答一个可以回答的问题:规范的哪一行证明了这种行为是正确的?

重载解析的工作方式如下:如果方法组包含泛型方法但未提供通用方法类型参数,则类型推断会尝试推断类型参数。如果无法推断类型参数,则不考虑重载决策的方法。在您的情况下,由于方法组中只有一个方法,因此删除它会导致重载解析失败。

为什么类型推断会失败?无法推断T,因为规范的控制线是:

  

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

stringV<string>没有标准隐式转化。这是用户定义的隐式转化。

因此类型推断失败。

我会在第二个回答中回答你的第二个问题。一般来说,在一个问题中提出两个问题是个坏主意。当你有两个问题时,可以发两个问题。

答案 2 :(得分:0)

我知道你的问题是“为什么类型推断不起作用”,但我只是认为我会在最后讨论你的两个选择。在这种情况下,我相信一个更好的替代隐式转换(我讨厌那些)是一个静态工厂方法。 IMO,当您致电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"));
}