Использование ссылки на общий объект в качестве параметра

У меня проблемы с передачей ссылки на объект общего типа. Я нашел способ обойти это путем создания «Объекта» и передачи ссылки на него, а не оригинала, но мне кажется, что он немного пахнет. Есть ли лучший способ здесь или я должен жить с этим?

Я понимаю первую ошибку, но вторая ускользает от меня.

public static T Foo<T>(ref T Bar)
{
    T Result;

    // Next line gives
    // cannot convert from 'ref T' to 'ref object'
    Result = (T)ModifyObject (ref Bar);

    // Next line gives
    // A ref or out argument must be an assignable variable
    Result = (T)ModifyObject (ref ((Object)Bar) );

    // Works
    Object Tmp = Bar;
    Result = (T)ModifyObject (ref Tmp) );

    return Result;

}

public static Object DoSomthing(ref Object Obj) {
    Object Result = Activator.CreateInstance (Obj.GetType ())
    //...
}

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

13.10.2009 07:07:08
Вы действительно должны передать это по ссылке? Внутри DoSomething, вы назначаете на Obj?
Henrik 13.10.2009 07:12:41
(См. Pobox.com/~skeet/csharp/parameters.html для получения дополнительной информации о реф.)
Jon Skeet 13.10.2009 07:15:46
В некоторых случаях Obj / Bar модифицируются. В других я передаю в качестве ссылки, чтобы избежать копирования того, что может быть большой структурой.
Courtney D 13.10.2009 07:20:01
@ Кортни: Во-первых, вы должны избегать больших типов значений, ИМО. «ref» должен использоваться, чтобы указать, что метод, вероятно, изменит значение параметра, чтобы не избежать копирования.
Jon Skeet 13.10.2009 07:22:24
Вы имели в виду отражение вместо рекурсии ?
Groo 13.10.2009 07:34:17
3 ОТВЕТА
РЕШЕНИЕ

Тип refаргумента должен соответствовать типу параметра. Вы не можете полагаться на неявные преобразования здесь. У Эрика Липперта есть сообщение в блоге: почему параметры ref и out не допускают изменения типа?

5
13.10.2009 07:11:56
Пока первая ошибка объясняется этим. Я не понимаю второго, где я явно приводю его к объекту (того же типа параметра, что и функция) перед применением оператора ref.
Courtney D 13.10.2009 07:26:45
@Courtney: refпараметр ожидает «переменную» объявленного типа, а не значение . Вы не можете передать 42 ref intпараметру. Приведения действуют на значения, а не на переменные. Вам нужно предоставить вызываемому место для проведения object. Это место не может быть intпеременной, например, потому что вызываемый объект может захотеть поместить stringв него.
Mehrdad Afshari 13.10.2009 07:31:29
Я понял сейчас :) - Значит, это лучшее решение, которое у меня есть?
Courtney D 13.10.2009 07:32:52
Если вам не нужно ref, то не используйте его. Вы могли бы хотеть передать тип во-первых, вместо фактического объекта.
Mehrdad Afshari 13.10.2009 08:00:23

Для второго примера вам нужно сначала привести его к переменной объекта:

object obj = (object)Bar;
Result = (T)ModifyObject (ref obj);

Но после того, как метод будет выполнен, единственное, что точно, это то, что objбудет иметь тип Object. Это то, о чем вас предупреждает компилятор.

А код делает запах немного. Если вы возвращаете результат типа T, то я не вижу причин для передачи параметра по ссылке. Во-вторых, вам не нужно передавать экземпляр вашего типа по ссылке для его создания. Этот метод будет работать просто отлично:

public static Object DoSomething(Type objType) {
    Object Result = Activator.CreateInstance(objType)
}

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

0
13.10.2009 07:37:35

Второе сообщение об ошибке уже содержит объяснение:

Аргумент ref или out должен быть присваиваемой переменной

Вот и все: вы создаете новый объект путем приведения: (Object) barможете ссылаться на тот же базовый объект, но, тем не менее, это другое значение. Кроме того, это временное значение, потому что вы никогда не присваивали ему отдельное имя переменной. Таким образом, передавать его по ссылке бессмысленно - любое изменение этого временного объекта будет потеряно. Таким образом, временные значения являются строго значениями: вы не можете присваивать им или передавать их по ссылке.

Именно поэтому ваш третий код работает: вы теперь связали результат преобразования с новым именем, и это имя можно использовать как lvalue, то есть его можно изменить (присвоить, передать по ссылке).

3
13.10.2009 07:36:41