В C# нет неявного преобразования типа int?
в тип int
.
Я определил следующий неявный оператор
namespace System
{
public partial struct Int32
{
public static implicit operator Int32(int? v)
{
return (Int32)(v ?? 0);
}
}
}
Что позволяет мне скомпилировать следующий код
int? nullableInt = 0;
Int32 regularInt = nullableInt;
но если я определяю regularInt
как int
вместо Int32
, я получаю следующую ошибку
Cannot implicitly convert type 'int?' to 'int'. An explicit conversion exists (are you missing a cast?)
Я ожидал, что int
и Int32
будут взаимозаменяемыми, но язык C# явно не был создан с учетом этой функциональности.
Есть ли техническая причина невозможности определить эту операцию, это решение принято для предотвращения потенциального запаха кода?
Я знаю, что определение такого неявного оператора может привести к очень неожиданному поведению, поскольку преобразование значения null
в целое число 0
не во всех ситуациях имеет смысл. Этот вопрос больше о том, «почему это нельзя сделать», чем о том, «почему делать это — плохая идея В самом деле».
Кроме того, как правило, неявные преобразования — это расширение, то есть вы можете неявно преобразовывать, если тип, в который вы преобразовываете, может выражать все те же значения, что и тип, из которого вы преобразовываете.
@RenéVogt «int и Int32 одинаковы», а не в показанном здесь коде, это не так.
Компилятор опередил вас, предпочтя выдать диагностику этой очень распространенной ошибки, прежде чем рассматривать оператор преобразования. Возможно, это можно считать ошибкой, но вполне вероятно, что Эрик Липперт не согласен :) В остальном хорошая демонстрация того, что int - это не просто «псевдоним» для Int32.
@HansPassant Поскольку они создали новый тип с тем же именем, что и системный тип, к которому они пытаются добавить неявное преобразование, вместо того, чтобы просто поместить неявное преобразование в тип, который не имеет общего имени ни с одним из типов, они просто получают поведение, которого они не ожидали, а не ошибка компилятора, о которой вы говорите.
Мой компилятор выдает CS0436: «тип Int32
в ThisTerribleIdea.cs
конфликтует с импортированным типом int
в mscorlib, ...
», что ясно демонстрирует вашу проблему. (И почему создание предупреждений об ошибках, как правило, является хорошей идеей в C#.) Конечно, система Int32
не partial
с самого начала, так что это никогда не сработает. Системные типы не являются «открытыми» в С#, и вы не можете добавлять что-либо. (Такой язык, как F#, относится к этому более спокойно.)
Добавляя комментарий @JeroenMostert, partial
— это вещь времени компиляции, и она работает только для двух типов в одной сборке. Компилятор использует оба частичных определения при компиляции типа. Это не способ добавления вещей к произвольным типам.
Частичные типы @JeroenMostert комбинируются только с другими частичными версиями этого типа из той же сборки, поэтому даже если системный Int32 был частичным, это не изменило бы того, что делает этот код.
@HansPassant Согласно спецификация языка, int
— это псевдоним для Int32
. У вас есть ссылка на ваше заявление?
@Servy Готов поспорить, что большинство людей не заметили, что сделал код вопроса, и приняли утверждение ОП, что это оператор неявного преобразования.
@КеннетК. Ганс прав. int
не является только что псевдонимом. Это ключевое слово C#, которое компилятор сопоставляет с System.Int32
. Существуют различные крайние случаи, когда разница вызывает проблемы, хотя и это один из них
@PanagiotisKanavos Я не понимаю вашу ссылку. Даже там написано, что int
— это псевдоним для Int32
(«В C# int — это просто псевдоним для System.Int32, поддерживаемый компилятором C#».). В нем также говорится, что int
— это нет псевдоним для CLR int32
, что, я думаю, не то, что говорится в этой ветке.
Код, который у вас есть, не добавляет неявное преобразование из .NET nullable int в .NET int. Он создает совершенно новый тип с именем Int32
в пространстве имен System
, но, поскольку он находится в другой сборке, чем Core.dll, это другой тип. (Взгляните на typeof(int).FullName
и typeof(int32).FullName
, чтобы увидеть это.)
Код, который вы показали для проверки этого неявного преобразования, настроен таким образом, что он пытается преобразовать системный тип, допускающий значение NULL, в ваш собственный новый тип, и, поскольку вы создали такое неявное преобразование, он преуспевает. Это не удается, когда вы используете системный тип вместо вашего собственного нового типа, потому что между этими типами нет неявного преобразования.
Вы не можете создавать неявные (или явные) преобразования для типов вне определения одного из этих типов, и поскольку вы не можете получить доступ к источнику ни Nullable
, ни .NET Int32, вы не можете добавить неявное преобразование.
Я не знал, что должен быть в определяющей сборке, я предполагал, что простое написание кода в правильном пространстве имен поможет. Спасибо, что доказали невозможность этой глупой идеи (не волнуйтесь, люди, я на самом деле не собираюсь это реализовывать, я просто люблю исследовать вещи. Это хороший способ учиться).
@mat Я понимаю вашу точку зрения, хотя это было бы довольно авантюрно использовать в повседневной практике, поскольку неявное значение по умолчанию null
на 0
может привести к некоторым трудным проблемам с поиском.
Как задокументировано здесь, неявные преобразования типов возможны только при сохранении преобразования.
Пример из прошлого
// Implicit conversion. A long can
// hold any value an int can hold, and more!
int num = 2147483647;
long bigNum = num;
В вашем случае int
или Int32
можно безопасно преобразовать в int?
, но не наоборот.
То же самое относится и к иерархии типов
class A {}
class B : A {}
class C : A {}
A var1 = new A(); // Compiles
A var2 = new B(); // Compiles (implicit cast)
B var3 = new B(); // Compiles
// need for explicit cast because instance2 can could also be A or C
B var4 = (B) instance2;
// Throws InvalidCastException
C var5 = (C) instance2;
@mat Я только что понял, что немного упустил суть твоего вопроса. Однако я оставлю это здесь, так как это добавляет некоторые базовые знания к ответу службы.
Поскольку значение по умолчанию для int равно 0, но int? по умолчанию имеет нулевое значение, которое не является допустимым значением int и приведет к исключению.
Например, интервал х; // результат объявления 0 инт? Икс; // результаты объявления равны нулю (недопустимое целочисленное значение)
Спасибо за ваше время, но это не отвечает на ваш вопрос, и ваше утверждение о том, что int x
имеет значение 0
, неверно. int x
создает неинициализированную переменную. Попробуйте выполнить любую арифметическую операцию с этой переменной x
, и вы получите ошибку компилятора.
@MathieuVIALES Если x
является членом класса или структуры, вам не нужна явная инициализация. Если это намерение Джоша, он должен так и сказать, чтобы избежать недоразумений.
Привет, @EdPlunkett, рад тебя видеть :) Хороший момент, хотя он мог бы указать это, добавив ключевое слово private
и используя соглашение об именах C# по умолчанию.
Лучшим примером может быть int defaultInt = default(int); int? defaultNullableInt = default(int?);
Потому что это не имеет смысла.
null
не равно 0.null
означает, что фактическое значение неизвестно. Это означает, что неявный оператор — плохая идея. Почему произвольное преобразование в 0? Почему неint.MinValue
или-1
?