Использование «ref» и/или «out» для типа объекта.

StackOverflow https://stackoverflow.com/questions/244826

  •  05-07-2019
  •  | 
  •  

Вопрос

Я застрял в приложении .Net 1.1 (т.е.Я пока не могу использовать дженерики из 2.0), и я пытался оптимизировать некоторые части кода.Поскольку здесь много работы с вызываемыми оболочками во время выполнения, которые необходимо освободить, в итоге я создал служебный метод, который работает в цикле до тех пор, пока не будут освобождены все ссылки.Сигнатура метода:

void ReleaseObject(object comObject)

После освобождения всех comObjects я вызываю GC.Collect и GC.WaitForPendingFinalizers (не спрашивайте — любой, кто имеет дело с взаимодействием с Office, знает).

И ...как обычно, я попал в крайний случай: если я не присвою соответствующей управляемой ссылке значение null перед вызовом GC.Collect, она не будет очищена должным образом.

Итак, мой код выглядит так:

ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...

Поскольку существует куча xxx=null, я решил поместить это в метод util, но поскольку существует разница между передачей по ссылке и передачей ссылочного параметра, очевидно, мне пришлось изменить метод на:

void ReleaseObject(out object comObject)
{
   //do release
   comObject = null;
}

и отредактируйте вызывающего абонента на:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

Это не удается с сообщением:«Невозможно преобразовать «out MyComClass» в «out object»»

Хотя я могу подумать, почему это может быть проблемой (т.обратное приведение объекта к MyComClass не является неявным, и нет никакой гарантии, что будет делать метод), мне было интересно, есть ли обходной путь, или мне нужно остаться с моими сотнями присвоений нулей.

Примечание:У меня есть куча разных типов COM-объектов, поэтому мне нужен параметр «объект», а не типобезопасный.

Это было полезно?

Решение

Почему лучше вызывать метод, чем просто установить переменную в null? Они оба - однолинейные, и последний намного проще.

Звучит очень странно, что вам нужно сначала установить их в null. Являются ли эти статические переменные или переменные экземпляра, значения которых должны быть освобождены раньше, чем содержащий их объект? Если переменная является просто локальной переменной, которая в любом случае выйдет из области видимости, установка ее на ноль не должна иметь никакого значения (в выпуске).

RCW не поддерживают IDisposable? Если это так, то лучше всего было бы позвонить в Dispose (желательно через оператор использования).

(после обсуждения в комментариях.)

Это локальные переменные, на которые позже не ссылаются в методе. Это означает, что сборщик мусора поймет, что его не нужно рассматривать как «root» ссылки - поэтому установка их на ноль не должна иметь никакого значения.

Однако, чтобы ответить на исходный вопрос напрямую: нет, вы не можете передать переменную по ссылке, если только параметр метода не имеет точно такой же тип, так что вам здесь не повезло. (С использованием дженериков это было бы возможно, но вы сказали, что ограничены .NET 1.1.)

Другие советы

Sunny, ref и out — это подсказки по сортировке + контракт с компилятором.Ref и out являются переносом во времена COM - подсказки по сортировке объектов при их отправке по сети/между процессами.

А out договор

void foo( out MyClass x)
  1. foo() установит x к чему-то, прежде чем оно вернется.
  2. x не имеет значения при вводе foo(), и вы получите ошибку компилятора, если попытаетесь использовать x прежде чем устанавливать его.(использование неназначенного выходного параметра x)

А ref договор

void foo( ref MyClass x)
  1. ref позволяет изменить ссылку вызывающего абонента.
  2. x должен быть назначаемым
    • вы не можете привести что-то к промежуточной переменной foo( ref (object) что-то)
    • x не может быть свойством

Реальность последних двух пунктов, скорее всего, помешает вам сделать то, что вы пытаетесь сделать, потому что по сути они не имеют смысла, когда вы понимаете, что на самом деле представляют собой ссылки.Если вы хотите это знать, спросите Джона Скита (он написал книгу).

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


ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ

Как отмечают другие, происходит что-то подозрительное.Похоже, что код грубой силы, который вы поддерживаете, содержит некоторые незначительные ошибки и страдает от кодирования по стечению обстоятельств.Лучшее решение, вероятно, — добавить еще один уровень косвенности.то естьОбертка для класса-оболочки, обеспечивающая детерминированную очистку, при которой вы можете написать беспорядочный код один и только один раз вместо того, чтобы разбрасывать его по всей кодовой базе.


Тем не менее..

Альтернатива 1

Ref не сработает, если вы не предоставите перегрузки для каждого типа (com) объекта, с помощью которого вы его будете вызывать.

// need a remove method for each type. 
void Remove( ref Com1 x ) { ...; x = null; }
void Remove( ref Con2 x ) { ...; x = null; }
void Remove( ref Com3 x ) { ...; x = null; }

// a generics version using ref.
void RemoveComRef<ComT>(ref ComT t) where ComT : class
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
    t = null; 
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
Remove( ref c1 );
RemoveComRef(ref c2); // the generics version again.

Альтернатива 2

Если вы не хотите этого делать, верните значение null из метода Remove() и верните его к типу объекта.

class Remover
{
    // .net 1.1 must cast if assigning
    public static object Remove(object x)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(x);
        return null;
    }

    // uses generics.
    public static ComT RemoveCom<ComT>(ComT t) where ComT : class
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
        return null;
    }   
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
c1 = (Com1)Remover.Remove(c1); // no reliance on generics
c2 = Remover.RemoveCom(c2); // relies on generics

* Я добавил общие версии для сравнения.

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


ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: все вышесказанное основано на необходимости вручную установить ссылку на значение null, что (обычно) не является необходимым.

По моему мнению, вы не сможете установить для этих объектов значение null в другом методе (кстати, вам нужно использовать параметр ref вместо out , чтобы сделать его в любом случае вы столкнетесь с той же проблемой с ошибкой " Не удалось преобразовать ... "). Я бы порекомендовал создать и массив объектов, а затем перебрать этот массив, вызвав метод ReleaseObject и установив для этих объектов значение null. Что-то вроде:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...

Вы должны звонить по Marshal.ReleaseComObject , который AFAIK был доступен в версии 1.1.

Вы, вероятно, имеете в виду " ref "

static void ReleaseObject(ref object comObject)
{
   if(comObject != null)
   {
     //do release
     comObject = null;
   }
}

[edit re comments], однако, это будет работать только для нетипизированных объектов, так что не стоит использовать без обобщений! Ох за C # 2.0 ...

Re " ref " ;; если переменные действительно переменные (то есть переменные метода), то они вскоре выйдут из области видимости и будут собраны. & Quot; ref " было бы полезно только освободить поля. Но, честно говоря, было бы проще установить их в нуль ...

Типичный шаблон COM:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top