Типы UInt32 и UInt64 не могут быть выведены из использования при использовании вместе с типом Int32 в универсальном методе
Изначально я столкнулся с этой проблемой, когда тестировал свой код с UnitTest framework с помощью Assert.Равнозначные методы. Я заметил, что для типов 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 не ведет себя так же?
P.S. Ой, чуть не забыл-я нашел эту таблицу преобразований типа , но во - первых - она для нового компилятора "Roslyn", а во-вторых-я все равно не вижу там ответа, может быть, кто-то укажет на нее?
2 ответов:
Поскольку
Является общим для обоих аргументов в
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 UInt64intиInt32иInt64. Если вы измените его на отрицательный 42, он не будет работать. (скорее, он мог бы , если бы вы запустили его в контекстеuncheckedи позволили номеру переполниться/обернуться.)
Когда вы просто предоставляете литерал, скажем
42, это всегда означаетInt32в c#. Чтобы ответить на ваш вопросПочему
integer.UInt32не может быть выведено?Поскольку
integer.UInt32имеет типUInt32, где as42имеет типInt32, поэтому компилятор не может делать предположений о том, что вы имели в виду, следовательно, он выдает ошибку.Вам нужно использовать суффикс
U, который заставляет компилятор правильно выводить его какUInt32MyGenericClass.DoNothing(42U, integer.UInt32);//No errorПочему
integer.UInt64не может быть сделал вывод?См. выше ответ, это же ожидать, что вам нужно использовать
ULсуффикс :)Почему
integer.UInt16можно ли вывести?Вы увидите, что
integer.UInt16на самом деле выводится какInt32, а неUInt16, потому что любое значениеUInt16может быть неявно преобразовано вInt32, поскольку его диапазон соответствуетInt32, который является0 - 65535, а также 42 являетсяInt32, поэтому компилятор счастлив вывести его какInt32, а неUInt16.
Comments