Почему конструктор имени/значения SqlParameter обрабатывает 0 как null?

Я заметил странную проблему в фрагменте кода, где нерегламентированный SQL-запрос не давал ожидаемого результата, хотя его параметры совпадали с записями в источнике данных. Я решил ввести в ближайшее окно следующее тестовое выражение:

new SqlParameter("Test", 0).Value

Это дало результат null, что заставило меня почесать голову. Кажется, что конструктор SqlParameter обрабатывает нули как пустые значения. Следующий код дает правильный результат:

SqlParameter testParam = new SqlParameter();
testParam.ParameterName = "Test";
testParam.Value = 0;
// subsequent inspection shows that the Value property is still 0

Кто-нибудь может объяснить такое поведение? Это как-то преднамеренно? Если так, то это потенциально довольно опасно...


person Bradley Smith    schedule 02.12.2011    source источник
comment
Дальнейшее чтение по этой теме: stackoverflow.com/questions/14224465/   -  person RLH    schedule 11.09.2014


Ответы (2)


Как указано в документации для этого конструктора:

Когда вы указываете объект в параметре значения, SqlDbType выводится из типа объекта Microsoft .NET Framework.

Будьте осторожны при использовании этой перегрузки SqlParameter для указания целочисленных значений параметров. Поскольку эта перегрузка принимает значение типа Object, необходимо преобразовать целочисленное значение для типа Object, когда значение равно нулю, как показано в следующем примере C#.

Parameter = new SqlParameter("@pname", (object)0);

Если вы не выполняете это преобразование, компилятор предполагает, что вы пытаетесь вызвать < strong>SqlParameter (string, SqlDbType) перегрузка конструктора.

Вы просто вызывали другой конструктор, чем вы думали в своем случае.

Причина этого в том, что C# допускает неявное преобразование из целочисленного литерала 0 в перечисляемые типы (которые ниже представляют собой целочисленные типы), и это неявное преобразование делает конструктор (string, SqlDbType) более подходящим для перегрузки. разрешение, чем бокс-преобразование, необходимое для преобразования int в object для конструктора (string, object).

Это никогда не будет проблемой при передаче переменной int, даже если значение этой переменной равно 0 (поскольку это не нулевой литерал) или любое другое выражение, имеющее тип int. Этого также не произойдет, если вы явно приведете int к object, как показано выше, потому что тогда будет только одна соответствующая перегрузка.

person Joey    schedule 02.12.2011
comment
У меня возникла эта проблема, и я попробовал другое решение, которое не сработало: почему предложенный Parameter = new SqlParameter("@pname", Convert.ToInt32(0)); работает, а Parameter = new SqlParameter("@pname", (int)0);, который я пробовал, не работает? - person mortb; 18.09.2014
comment
@mortb, вам нужно будет проконсультироваться со спецификацией языка для этого, но я предполагаю, что разрешение перегрузки будет заботиться о точном типе в первом случае, но может разрешить перечисления во втором, потому что вы все еще передаете литерал типа int. - person Joey; 18.09.2014
comment
Я выяснил, почему: неправильное поведение компилятора c Sharp с перегрузкой метода и "> stackoverflow.com/questions/3153841/ Литерал 0 всегда будет оцениваться как соответствующий типу перечисления, а другие числа - нет. Кажется немного непонятным... - person mortb; 19.09.2014
comment
Что-то не так с процитированным примером: Convert.ToInt32(0) не меняет выбранную перегрузку. Код должен быть new SqlParameter("@pname", (object)0);. - person Steven Liekens; 18.03.2016
comment
@Steven: Поправьте меня, если я ошибаюсь, но только литерал 0 может быть неявно преобразован в любой тип перечисления. Таким образом, вызов метода, который возвращает int, подходит и выбирает другую перегрузку. По общему признанию, прошло несколько месяцев с тех пор, как я просматривал спецификацию, но на самом деле это сработало именно так. - person Joey; 18.03.2016
comment
Ты прав. Но все равно это выглядит странно, и я бы не советовал так делать. Кто-то, глядя на код, может удивиться, почему значение int заключено в Convert.ToInt32(), и удалить окружающий вызов, не зная, что компилятор должен делать правильные вещи. - person Steven Liekens; 18.03.2016
comment
@Steven: Очевидно, документация переключилась с рекомендации Convert.ToInt32 на простое приведение object с .NET 4 (в более старых версиях документации по-прежнему отображается вызов Convert). Поскольку в любом случае необходимо преобразование в бокс, приведение сохраняет вызов метода и, вероятно, немного понятнее (учитывая, что мало кто читает спецификацию C#). Я скорректирую ответ и немного расширим его. - person Joey; 18.03.2016
comment
Если 0 преобразуется в тип перечисления, то почему 1 не преобразуется в тип перечисления? - person Anders Lindén; 30.04.2020
comment
@AndersLindén: потому что спецификация языка C # предписывает, что только числовой целочисленный литерал 0 можно неявно преобразовать в любой тип перечисления. При необходимости любой другой литерал или выражение можно преобразовать явно. Почему они приняли такое решение, я не могу сказать. - person Joey; 30.04.2020

Рекомендуется использовать типизированные данные при передаче/добавлении параметров.

Ниже вы можете выполнить задачу, как показано ниже:

Для данных типа string/varchar:

SqlParameter pVarchar = new SqlParameter
                    {
                        ParameterName = "Test",
                        SqlDbType = System.Data.SqlDbType.VarChar,
                        Value = string.Empty,
                    };

Для данных типа int:

SqlParameter pInt = new SqlParameter
                    {
                        ParameterName = "Test",
                        SqlDbType = System.Data.SqlDbType.Int,
                        Value = 0,
                    };

Вы можете изменить значение SqlDbType в соответствии с используемыми данными.

person Elias Hossain    schedule 02.12.2011
comment
Хотя в целом это полезно, технически это не ответ на вопрос. Таким образом, это должен быть комментарий. - person Joey; 02.12.2011