最初,当我使用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;编译器,其次 - 无论如何我都没有看到答案,也许有人会指出它?
答案 0 :(得分: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
的共享范围内。如果您将其更改为否定 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
。