2010-08-21 3 views
29

Чем больше я вижу ref, используемого в производственном коде, тем больше злоупотреблений я встречаю и чем больше боли вызывает у меня. Я ненавижу это ключевое слово, потому что с точки зрения каркасного построения это кажется глупым. Когда было бы неплохо сообщить пользователям вашего кода понятие , возможно, изменить ссылку на объект/значение из-под них?Когда используется ключевое слово C# ref всегда хорошая идея?

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

Я, однако, не мудрый разработчик; Я уверен, что это практически применимо. Я просто хотел бы узнать, кто они.

ответ

31

Рекомендации по проектированию Рамочные (книга Кшиштоф Квалина и Брэдом Abrams) рекомендуют избегать как параметров ref, так и out.

ИЗБЕГАТЬ использованием out или ref параметров.

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

Руководство Дизайн Рамочные процитировать канонический Swap метод в качестве действительного исключения:

void Swap<T>(ref T obj1, ref T obj2) 
{ 
    T temp = obj1; 
    obj1 = obj2; 
    obj2 = temp; 
} 

, но в то же время комментарий замечает

Своп всегда приходит в этих дискуссиях, но Я не написал код, который действительно нуждался в своп-методе, начиная с колледжа.Если у вас нет веской причины, избегайте out и ref.

+17

Я предполагаю, что Cwalina и Abrams не консультировались, когда были разработаны методы TryParse. :) –

+2

, так что IDictionary .TryGetValue метод. Это не так убедительно, если .NET framework не следует их предложению. –

+4

@D Hoerster - на самом деле книга обсуждает шаблон TryParse в положительном свете. Это рекомендация «избегать», то есть есть известные случаи, когда нарушение правила имеет смысл. – TrueWill

-1

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

Пример:

getTreeNodeValues(ref selectedValue, ref selectedText); 

Edit:

Это лучше использовать здесь - так прокомментировал.

getTreeNodeValues(out selectedValue, out selectedText); 

Я использую его для обработки объектов:

MyCar car = new MyCar { Name="TestCar"; Wieght=1000; } 

UpdateWeight(ref car, 2000); 
+12

Вы должны использовать 'out' ключевое слово при возврате нескольких значений. 'ref' действительно не подходит. –

+3

Вы должны обязательно использовать 'out', как указано. Но вы также должны рассмотреть возможность использования вспомогательного объекта.Если у вас более двух возвращаемых значений, это, как правило, более чистая стратегия. – Timwi

+1

Я думаю, что это хороший пример для претензии: «разница между параметрами out и ref не широко понята», сделанная в ответе dtb. – Heinzi

5

В любое время вы хотите изменить значение значения типа - это часто бывает в тех случаях, когда вы хотите, чтобы эффективно обновить пара соответствующих значений (то есть, а не возвращать структура, содержащая два Интс, вы передаете (исх INT х, исх INT у))

+1

В чем преимущество использования ref при сравнении с отсутствием? – silvo

+2

out означает, что объект должен быть инициализирован первым, с ref, это необязательно. – Malfist

+10

@ Малфист Я думаю, что у вас есть это в обратном направлении. – siride

0

Это полезно, когда вам нужны эффективные алгоритмы на месте на бонусах.

+1

Профилированы ли вы и определили, что это ваше узкое место в производительности? – TrueWill

+0

@TrueWill: Да, на самом деле. – Charles

2

Может быть, когда у вас есть-структура (который является типом значения):

struct Foo 
{ 
    int i; 

    public void Test() 
    { 
     i++; 
    } 
} 

static void update(ref Foo foo) 
{ 
    foo.Test(); 
} 

и

Foo b = new Foo(); 
update(ref b); 

Здесь вы бы использовать два-параметры с out как:

static void update(Foo foo, out Foo outFoo) //Yes I know you could return one foo instead of a out but look below 
{ 
    foo.Test(); 

    outFoo = foo; 
} 

, используя метод, имеющий более одного Foo, тогда вы получите два параметра с out против ref. Альтернативой является возврат N-кортежа. У меня нет реального примера, когда использовать этот материал.

Добавить на: Различные .TryParse методы также могли бы избежать, если out они вернулись Nullable<T> вместо, который по существу представляет собой кортеж boolean * T.

+0

О, человек, который может быть опущен на TryParse, сделает адский метод расширения, я уверен. – bwerks

+2

Возвращение объекта или null и ожидание вызова вызывающего абонента является рискованным. У нас есть более старые API-интерфейсы, которые это делают, и я видел слишком много клиентского кода, который сразу вызывает методы для возвращаемого «объекта». Бросок на отказ и/или шаблон TryParse - это, как правило, лучшие варианты. (TryParse не устраняет глупых ошибок, но это мешает им.) – TrueWill

+0

@bwerks Я это сделал. @TrueWill Возможно, вы правы, но вы должны использовать '??', чтобы пользователи знали. Я использовал метод расширения, например: 'jf (Request [" SomeHeader "]. ToBoolean() == true)', который я действительно считаю приятным. Он также работает, когда заголовок имеет значение NULL, но может быть и не ясно, что он делает. –

9

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

т.е.

var x = ..... 
var y = ..... 
// some local code... 
var z = DoSomethingSpecific(ref x, ref y); // needs and updates x/y 
// more local code... 

и т. Д. Где DoSomethingSpecific - это частный метод, который просто перемещен, чтобы поддерживать управляемость метода.

13

Большинство параметров используют ref параметры (я уверен, вы согласны).

0

Гипотетически, я бы предположил, что вы можете использовать множество аргументов ref/out, если вы намеревались имитировать архитектуру старого процессуального программного обеспечения, например, старых игровых движков и так далее. Я отсканировал исходный код одного, я думаю, что это был Duke Nukem 3D, и это процедурно с множеством подпрограмм, изменяющих переменные на месте, и почти никаких функций. Очевидно, что вряд ли вы будете программировать это для реального производственного приложения, если у вас нет определенной цели.

0

Как насчет того, хотите ли передать массив функции, которая могла бы или не могла изменить ее размер и сделать что-то еще для этого. Часто можно было бы обернуть массив в другом объекте, но если кто-то хочет обработать массив, непосредственно проходящий по ссылке, будет казаться наиболее естественным.

0

Другой полезный пример того, чтобы поменять <> это:

Prompter.getString("Name ? ", ref firstName); 
Prompter.getString("Lastname ? ", ref lastName); 
Prompter.getString("Birthday ? ", ref firstName); 
Prompter.getInt("Id ? ", ref id); 
Prompter.getChar("Id type: <n = national id, p = passport, d = driver licence, m = medicare> \n? ", ref c); 



public static class Prompter 
{ 
    public static void getKey(string msg, ref string key) 
    { 
     Console.Write(msg); 
     ConsoleKeyInfo cki = Console.ReadKey(); 
     string k = cki.Key.ToString(); 
     if (k.Length == 1) 
      key = k; 
    } 

    public static void getChar(string msg, ref char key) 
    { 
     Console.Write(msg); 
     key = Console.ReadKey().KeyChar; 
     Console.WriteLine(); 
    } 

    public static void getString(string msg, ref string s) 
    { 
     Console.Write(msg); 
     string input = Console.ReadLine(); 
     if (input.Length != 0) 
      s = input; 
    } 

    public static void getInt(string msg, ref int i) 
    { 
     int result; 
     string s; 

     Console.Write(msg); 
     s = Console.ReadLine(); 

     int.TryParse(s, out result); 
     if (result != 0) 
      i = result;  
    } 

    // not implemented yet 
    public static string getDate(string msg) 
    { 
     // I should use DateTime.ParseExact(dateString, format, provider); 
     throw new NotImplementedException(); 
    }  


} 

Используйте здесь это не вариант

Смежные вопросы