在泛型方法中与Int32类型一起使用时,无法根据用法推断出UInt32和UInt64类型

时间:2014-07-18 10:22:15

标签: c# generics compiler-construction type-conversion

最初,当我使用Assert.AreEqual方法使用UnitTest框架测试代码时,我遇到了这个问题。我注意到,对于UInt32和UInt64类型,选择了AreEqual的不同重载(AreEqual(object,object)而不是AreEqual< T>(T,T))。我做了一些研究,得到了以下简单的代码:

public struct MyInteger
{
    public SByte SByte { get; set; }
    public Byte Byte { get; set; }
    public UInt16 UInt16 { get; set; }
    public UInt32 UInt32 { get; set; }
    public UInt64 UInt64 { get; set; }
    public Int16 Int16 { get; set; }
    public Int32 Int32 { get; set; }
    public Int64 Int64 { get; set; }
}

public class MyGenericClass
{
    public static void DoNothing<T>(T expected, T actual)
    {
    }
}

public class IntegerTest
{
    public void TestIntegers()
    {
        var integer = new MyInteger
        {
            SByte = 42,
            Byte = 42,
            Int16 = 42,
            Int32 = 42,
            Int64 = 42,
            UInt16 = 42,
            UInt32 = 42,
            UInt64 = 42
        };
        MyGenericClass.DoNothing(42, integer.SByte); // T is Int32
        MyGenericClass.DoNothing(42, integer.Byte); // T is Int32
        MyGenericClass.DoNothing(42, integer.Int16); // T is Int32
        MyGenericClass.DoNothing(42, integer.Int32); // T is Int32
        MyGenericClass.DoNothing(42, integer.Int64); // T is Int64
        MyGenericClass.DoNothing(42, integer.UInt16); // T is Int32
        MyGenericClass.DoNothing(42, integer.UInt32); // Error
        MyGenericClass.DoNothing(42, integer.UInt64); // Error
        MyGenericClass.DoNothing((UInt32)42, integer.UInt32); // T is UInt32
        MyGenericClass.DoNothing((UInt64)42, integer.UInt64); // T is UInt64
    }
}   

我得到的错误信息是&#34;方法的类型参数&#39; MyGenericClass.DoNothing&lt; T&gt;(T,T)&#39;无法从使用中推断出来。尝试明确指定类型参数。&#34;。解决方法相对简单(使用显式转换),所以我只想知道,UInt32和UInt64有什么特别之处,其他类型没有(或者有),以及为什么UInt16的行为方式不同?
附:哦,我差点忘了 - 我已经找到了conversions这个类型的表,但首先是 - 它适用于新的&#34; Roslyn&#34;编译器,其次 - 无论如何我都没有看到答案,也许有人会指出它?

2 个答案:

答案 0 :(得分:3)

因为T的两个参数都共享DoNothing,所以只有存在公共基类或隐式运算符才能转换为两个输入的公共类时,编译器才能使用它。

之所以出现这两个错误,是因为UInt32UInt64Int3242字面值)没有隐式转换。

没有隐式转换的原因是因为两者之间可能存在信息丢失。所有其他转换共享一个共同的值范围。例如,UInt16的范围为0到65535,范围为正常intByte也是如此,可以表示为0-7。 C#认为这些类型的转换是“安全的”,因此可以隐式执行。但UInt32可以从0到4,294,967,295,这是普通int可能的两倍。 C#认为这类转换不安全,需要您执行explicit强制转换。其语义表示您期望此转换在某些情况下可能会失败(值超出兼容范围)。

DoNothing(42, integer.SByte); // Converts SByte to Int32
DoNothing(42, integer.Byte); // Converts Byte to Int32
DoNothing(42, integer.Int16); // Converts Int16 to Int32
DoNothing(42, integer.Int32); // Already the same
DoNothing(42, integer.Int64); // Converts Int32 to Int64
DoNothing(42, integer.UInt16); // Converts UInt16 to Int32
DoNothing(42, integer.UInt32); // Error - no implicit conversion
DoNothing(42, integer.UInt64); // Error - no implicit conversion
DoNothing((UInt32)42, integer.UInt32); // Explicitly converted to UInt32
DoNothing((UInt64)42, integer.UInt64); // Explicitly converted to UInt64

显式转换有效,因为您故意选择的数字位于intInt32以及Int64的共享范围内。如果您将其更改为否定 42,则无效。 (相反,如果你在unchecked上下文中运行可以,并让数字溢出/回绕。)

答案 1 :(得分:2)

当您提供文字时,请说42,它总是表示c#中的Int32。回答你的问题

  

为什么integer.UInt32无法推断?

因为integer.UInt32的类型为UInt32,其中42的类型为Int32,因此编译器无法对您的意思做出假设,因此会产生错误。< / p>

您需要使用U后缀,这会使编译器正确推断为UInt32

MyGenericClass.DoNothing(42U, integer.UInt32);//No error
  

为什么integer.UInt64无法推断?

参考上面的答案,同样期望你需要使用UL后缀:)

  

为什么可以推断integer.UInt16

您会看到integer.UInt16实际上被推断为Int32而不是UInt16,因为任何UInt16值都可以隐式转换为Int32,因为它的范围适合Int32 0 - 65535,而42也是Int32,因此编译器很乐意将其推断为Int32而不是UInt16