Типы 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", а во-вторых-я все равно не вижу там ответа, может быть, кто-то укажет на нее?

646   2  

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 UInt64
Явное преобразование сработало, потому что вы намеренно выбрали число, которое было в пределах общего диапазона int и Int32 и Int64. Если вы измените его на отрицательный 42, он не будет работать. (скорее, он мог бы , если бы вы запустили его в контексте unchecked и позволили номеру переполниться/обернуться.)

Когда вы просто предоставляете литерал, скажем 42, это всегда означает Int32 в c#. Чтобы ответить на ваш вопрос

Почему integer.UInt32 не может быть выведено?

Поскольку integer.UInt32 имеет тип UInt32, где as 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.

Comments

    Ничего не найдено.