Неявные операторы и присвоения SqlParameter.Value с ошибкой IConvertible

У меня есть тип члена класса Int32, но требуется, чтобы метод .ToString() этого члена возвращал специальную отформатированную версию Int32. Поэтому я создал структуру с неявными операторами, которая сохраняет семантику простого присваивания значений, и я могу переопределить .ToString()...

public struct MyValue
{
    private int _val;

    public MyValue(int val) { _val = val; }

    public static implicit operator int(MyValue value)
    {
        return value._val;
    }

    public static implicit operator MyValue(int value)
    {
        return new MyValue(value);
    }

    public override string ToString()
    {
        return _val.ToString("0000000000");
    }
}

При обычном использовании он работает так, как ожидалось. Однако, когда я назначаю элемент SqlParameter.Value, я получаю ошибку "... use IConvertible..."...

string connString = "Server=svr;Database=db;Trusted Connection=Yes";
string sql = "SELECT * FROM [tbl] WHERE [val] = @val";

MyValue val = 256;

SqlParameter parm = new SqlParameter("@val", SqlDbType.Int);
parm.Value = val;

using ( SqlConnection conn = new SqlConnection(connString) )
{
    conn.Open();
    using ( SqlCommand cmd = new SqlCommand(sql, conn) )
    {
        cmd.Parameters.Add(parm);
        using ( SqlDataReader rdr = cmd.ExecuteReader() )
        {
            while ( !rdr.Read() ) { }
            rdr.Close();
        }
    }
    conn.Close();
}

Во время отладки похоже, что метод .ToString() в моей структуре вызывается при назначении SqlParameter.Value. Итак, вопрос № 1: почему он делает это вместо того, чтобы присваивать базовое значение Int32?

Связанный с этим вопрос № 2: почему начальное назначение не генерирует ошибку, поскольку ошибка генерируется в методе SqlCommand.ExecuteReader()?

Не стесняйтесь критиковать любой аспект кода, который я написал, но я действительно надеюсь получить хорошие ответы на свои вопросы.

(отредактировано для добавления) Я перефразирую свою проблему...

Мне нужно создать тип данных значения, который точно имитирует Int32 (с исключением .ToString()), чтобы, когда он помещается в коробку, значение Int32 помещалось в коробку вместо структуры.

Это возможно?


person Chris Gallucci    schedule 04.01.2013    source источник
comment
Вместо "0000000000" вы также можете использовать "D10".   -  person Jeppe Stig Nielsen    schedule 05.01.2013
comment
потому что вы назначаете Struct объекту.... сделайте структуру классом   -  person Dan Hunex    schedule 05.01.2013
comment
@DanHunex Нет, я не думаю, что это поможет.   -  person Jeppe Stig Nielsen    schedule 05.01.2013
comment
Кто знает, с какими самодельными типами может работать код в System.Data.SqlClient? Согласно сообщению об ошибке, это может работать, если ваша структура реализует интерфейс IConverible. Int32 реализует это, поэтому вы можете перенаправить каждый вызов на явную реализацию интерфейса Int32. Но это очень уродливый код. Как я уже говорил в другом месте, почему бы просто не сказать parm.Value = (int)val; в таких случаях?   -  person Jeppe Stig Nielsen    schedule 05.01.2013
comment
И добавить, что это проблема дизайна на данный момент.   -  person Chris Gallucci    schedule 05.01.2013
comment
Я перефразирую свою проблему... Мне нужно создать тип данных значения, который точно имитирует Int32 (с исключением .ToString()), чтобы, когда он помещается в коробку, значение Int32 помещалось в коробку вместо структуры. Это возможно?   -  person Chris Gallucci    schedule 07.01.2013


Ответы (1)


SqlParameter.Value имеет тип object, поэтому компилятор не знает, как вызвать неявный оператор для преобразования экземпляра MyValue val в int.

Однако что-то вроде следующего должно работать:

static SqlParameter GetIntSqlParameter(string name, int value)
{
    SqlParameter parm = new SqlParameter(name, SqlDbType.Int);
    parm.Value = value;
    return parm;
}

который можно назвать:

SqlParameter parm = GetIntSqlParameter("@val", val);

или, как указал Йеппе в комментариях, приведение к int также работает (но тогда оператор также может быть определен как explicit.

Я был слишком занят, печатая мини-разглагольствования о том, почему я ненавижу неявные преобразования.

person jam40jeff    schedule 04.01.2013
comment
В яблочко! Поэтому вместо parm.Value = val; он должен сказать parm.Value = (int)val;, потому что мы знаем, что все может обрабатывать int (также Int32 реализует IConvertible). - person Jeppe Stig Nielsen; 05.01.2013
comment
Да, и кастинг, и реализация IConvertible работают. Наши требования запрещают кастинг, и я пытаюсь найти способ отказаться от реализации IConvertible только из-за всего лишнего шума. - person Chris Gallucci; 05.01.2013