Лучше использовать для нескольких выходных значений или вернуть комбинированный тип значения? - PullRequest
14 голосов
/ 11 марта 2011

Например, в соответствии с:

public bool Intersect (Ray ray, out float distance, out Vector3 normal)
{

}

против

public IntersectResult Intersect (Ray ray)
{

}

public class IntersectResult
{
    public bool Intersects {get;set;}
    public float Distance {get;set;}
    public Vector3 Normal {get;set;}
}

Что лучше с точки зрения ясности, простоты использования и, что наиболее важно, производительности.

Ответы [ 8 ]

16 голосов
/ 11 марта 2011

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

IEnumerable<Ray> rays = GetAThousandRays();
var intersections = from ray in rays 
                    where Intersect(ray, out distance, out normal)
                    orderby distance ...

Выполнение запроса теперь , неоднократно мутировав те же две переменные .Вы делаете заказ на основе значения, которое мутирует.Это беспорядок.Не делайте запросов, которые изменяют вещи;это очень сбивает с толку.

То, что вы хотите:

var intersections = from ray in rays 
                    let intersection = Intersect(ray)
                    where intersection.Intersects
                    orderby intersection.Distance ...

Нет мутации;манипулировать последовательностью значений как значениями , а не как переменными .

Я также был бы склонен, возможно, избавиться от этого логического флага и сделать значение неизменной структурой:

// returns null if there is no intersection
Intersection? Intersect(Ray ray) { ... }

struct Intersection 
{
    public double Distance { get; private set; }
    public Vector3 Normal { get; private set; }
    public Intersection(double distance, Vector3 normal) : this()
    {
        this.Normal = normal;
        this.Distance = distance;
    }
} 
10 голосов
/ 11 марта 2011

Я бы использовал комбинированный тип.

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

6 голосов
/ 11 марта 2011

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

5 голосов
/ 11 марта 2011

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

Если вам когда-нибудь понадобится добавить еще один бит данных в качестве вывода, вам придется изменить сигнатуру метода в дополнение к добавлению кода для дополнительного ответа;не так со сложным типом вывода.Кроме того, сложнее смоделировать (для модульного тестирования) выходные параметры.Также кажется, что это нарушает «простую» философию - вам приходится сталкиваться с проблемой настройки выходных параметров, когда вам нужен только один элемент ввода.Сила языка ООП заключается в создании типов - придерживайтесь этого пути, по моему мнению.

Разница в производительности между ними незначительна.

3 голосов
/ 11 марта 2011

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

Очевидно, что во втором примере представлены первые двав то время как первый обеспечивает лучшую производительность.Является ли это незначительным, я не знаю.Может быть, запустите бенчмарк-тест и выясните.

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

3 голосов
/ 11 марта 2011

Вы можете использовать Tuple вместо создания класса IntersectResult в качестве альтернативы.

Ссылка:

http://msdn.microsoft.com/en-us/library/system.tuple.aspx

Однако я бы определенно склонялся к возвращениюсложный тип по выходным параметрам.

2 голосов
/ 11 марта 2011

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

1 голос
/ 11 марта 2011

Это полностью зависит от того, насколько последовательна концепция IntersectResult.

Технически нет причин отдавать предпочтение одному над другим.

...