最初,当我使用 Assert.AreEqual 方法通过 UnitTest 框架测试代码时,我遇到了这个问题。我注意到,对于 UInt32 和 UInt64 类型,选择了不同的 AreEqual 重载(AreEqual(object, object) 而不是 AreEqual
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
附:哦,我差点忘了 - 我找到了这个conversions类型的表,但首先 - 它是用于新的“Roslyn”编译器,其次 - 无论如何我都没有看到答案,也许有人会看到指出来吗?
因为
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
上下文中运行它并让数字溢出/环绕,它可以。)
当你只提供一个文字时,说
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
。