Передача объекта IDisposable по ссылке вызывает ошибку? - PullRequest
2 голосов
/ 27 апреля 2009

Я пытаюсь создать общий метод для удаления объекта, который реализует IDisposable , называемый DisposeObject()

Чтобы убедиться, что я удаляю объект, указанный исходной ссылкой, я пытаюсь передать объект по ссылке.

Но я получаю ошибку компиляции, которая говорит

Тип аргумента 'ref' не соответствует типу параметра

В приведенном ниже (упрощенном) коде оба _Baz и _Bar реализуют IDisposable .

alt text

Итак, вопросы,

  1. Почему я получаю эту ошибку?
  2. Есть ли способ обойти это?

[UPDATE] Из предоставленных ответов до тех пор, пока я не установлю аргумент IDisposable в значение null, я могу просто передать объект по значению без использования ref. У меня теперь есть другая проблема, устанавливать ли одноразовые объекты на null или нет в методе DisposeObject.

Вот полный источник полноты:

public class Foo : IDisposable
{
    private Bar _Bar;
    private Baz _Baz;
    private bool _IsDisposed;

    ~Foo() { Dispose(false); }

    public void Dispose(bool disposing)
    {
        if (!_IsDisposed)
        {
            if (disposing)
            {
                DisposeObject(ref _Baz);
                DisposeObject(ref _Bar);
            }
        }

        _IsDisposed = true;
    }

    private void DisposeObject(ref IDisposable obj)
    {
        try
        {
            if (obj == null) 
                return;
            obj.Dispose();
            obj = null;
        } catch (ObjectDisposedException) { /* Already Disposed... */ }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public class Bar : IDisposable
{
    public void Dispose() {}
}

public class Baz : IDisposable
{
    public void Dispose() {}
}

[РЕЗУЛЬТАТ]
Я удалил код, который устанавливает аргумент в null (obj = null;) в DisposeObject Таким образом, окончательный код стал.

    public void Dispose(bool disposing)
    {
        if (!_IsDisposed)
        {
            if (disposing)
            {
                DisposeObject(_Baz);
                DisposeObject(_Bar);
            }
        }

        _IsDisposed = true;
    }

    private void DisposeObject(IDisposable obj)
    {
        try
        {
            if (obj == null) 
                return;
            obj.Dispose();
        } catch (ObjectDisposedException) { /* Already Disposed... */ }
    }

Ответы [ 5 ]

5 голосов
/ 27 апреля 2009

Вот вариант для вашего примера (не могу сейчас проверить его по компилятору, но вы поймете):

private void DisposeObject<T>(ref T obj) where T : IDisposable
{
    // same implementation
}

Чтобы позвонить, используйте

DisposeObject<Baz>(ref _Baz);
DisposeObject<Bar>(ref _Bar);

Как указано в других комментариях, ошибка компилятора, которую вы получаете, имеет свое собственное назначение (не позволяющее назначить какой-либо другой тип IDisposable внутри вашего метода, что приводит к несовместимому состоянию).

4 голосов
/ 27 апреля 2009

Нет необходимости передавать по ссылке, так как вы передаете ссылочный тип. Вы должны удалить ключевое слово ref из определения вашего метода. Сделайте это, и у вас не должно возникнуть никаких проблем, хотя я не уверен, насколько это эффективнее или понятнее, чем простой вызов Dispose() (кроме того факта, что вам не нужно приводить его для явных реализаций, и это делает null чек для вас).

Редактировать

Танцуйте, хотя я надеюсь, что обсуждение этой темы было полезным для вас, ваше первоначальное намерение не кажется выполнимым. Чтобы передать что-то как ref, вы не можете передать переменную, тип которой отличается от того, что ожидает параметр ref (другими словами, вы не можете передать переменную, объявленную как class или другое interface, который реализует IDisposable, если параметр ref равен IDisposable). Поскольку параметры ref позволяют распространять назначения обратно на вызывающую сторону, вы открываете возможность хранения несовместимых типов в вашей переменной.

Лучше всего назначить null самостоятельно, если вы этого хотите. Если вы хотите инкапсулировать проверку null и игнорировать исключения в функции, это нормально, но ref не будет работать для вас в этом сценарии, к сожалению, независимо от того, как вы его нарежете.

4 голосов
/ 27 апреля 2009

Попробуйте это:

IDisposable d = (IDisposable)_Baz;
DisposeObject(ref d);

Редактировать : Как указывает Адам, ваш код не требует, чтобы это было ref. Объекты всегда передаются как ссылки.

2 голосов
/ 27 апреля 2009

Этот подход пахнет смешно, но я пока проигнорирую это.

Чтобы решить вашу проблему, вам нужно наложить объекты, которые вы передаете с помощью «(IDisposable)»

Я уступаю воле компилятора и Джона Скита. Для этого вам нужен реальный объект:

IDisposable _BazD = (IDisposable)_Baz;
DisposeObject(ref _BazD);

Я бы также добавил нулевую проверку в вашем DisposeObject () в дополнение к try / catch. «Obj == null» будет быстрой и простой проверкой по сравнению с дорогостоящим перехватом исключений, если это будет несколько раз попадать для одного и того же объекта. Хм ... это было минуту назад? Nevermind.

0 голосов
/ 13 мая 2010

Спасибо, Дэн С. У меня еще недостаточно представителей, чтобы добавлять комментарии, поэтому я должен добавить это в качестве ответа. Тем не менее, полная благодарность Dan C за это решение.

Это рабочий код:

public override void Dispose()
{
    base.Dispose();

    DisposeOf<UserTableAdapter>(ref userAdapter);
    DisposeOf<ProductsTableAdapter>(ref productsAdapter);

    if (connection != null)
    {
        if (connection.State == ConnectionState.Open)
        {
            connection.Close();
        }
        DisposeOf<SqlConnection>(ref connection);
    }
}

private void DisposeOf<T>(ref T objectToDispose) where T : IDisposable
{
    if (objectToDispose != null)
    {
        objectToDispose.Dispose();
        objectToDispose = default(T);
    }
}
...