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

问题描述 投票:0回答:2

最初,当我使用 Assert.AreEqual 方法通过 UnitTest 框架测试代码时,我遇到了这个问题。我注意到,对于 UInt32 和 UInt64 类型,选择了不同的 AreEqual 重载(AreEqual(object, object) 而不是 AreEqual(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
    }
}   

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

c# generics type-conversion
2个回答
3
投票

因为

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

出现这两个错误是因为

UInt32
UInt64
Int32
(您的
42
文字)之间没有隐式转换。

没有隐式转换的原因是两者之间可能存在信息丢失。所有其他转换都共享一个共同的值范围。例如,

UInt16
的范围为0到65535,这在正常
int
的范围内。
Byte
也是如此,可以表示为
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

显式转换之所以有效,是因为您故意选择了

int
Int32
Int64
共享范围内的数字。如果你把它改成Negative 42,那就不行了。 (相反,如果您在 unchecked 上下文中运行它并让数字溢出/环绕,它
可以
。)


2
投票

当你只提供一个文字时,说

42
它总是意味着 c# 中的
Int32
。回答你的问题

为什么无法推断

integer.UInt32

因为

integer.UInt32
UInt32
类型,而
42
Int32
类型,所以编译器无法对你的意思做出假设,因此会产生错误。

您需要使用

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

© www.soinside.com 2019 - 2024. All rights reserved.