Каков наилучший способ вернуть два значения из метода? - PullRequest
10 голосов
/ 05 мая 2010

Когда мне нужно написать методы, которые возвращают два значения , я обычно делаю это, как в следующем коде, который возвращает List<string>. Или если я должен вернуться, например id и строка , затем я возвращаю List<object>, а затем выбираю их с index number и recast values.

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

using System;
using System.Collections.Generic;
using System.Linq;

namespace MultipleReturns
{
    class Program
    {
        static void Main(string[] args)
        {
            string extension = "txt";

            {
                List<string> entries = GetIdCodeAndFileName("first.txt", extension);
                Console.WriteLine("{0}, {1}", entries[0], entries[1]);
            }

            {
                List<string> entries = GetIdCodeAndFileName("first", extension);
                Console.WriteLine("{0}, {1}", entries[0], entries[1]);
            }

            Console.ReadLine();
        }

        /// <summary>
        /// gets "first.txt", "txt" and returns "first", "first.txt"
        /// gets "first", "txt" and returns "first", "first.txt"
        /// it is assumed that extensions will always match
        /// </summary>
        /// <param name="line"></param>
        public static List<string> GetIdCodeAndFileName(string line, string extension)
        {
            if (line.Contains("."))
            {
                List<string> parts = line.BreakIntoParts(".");
                List<string> returnItems = new List<string>();
                returnItems.Add(parts[0]);
                returnItems.Add(line);
                return returnItems;
            }
            else
            {
                List<string> returnItems = new List<string>();
                returnItems.Add(line);
                returnItems.Add(line + "." + extension);
                return returnItems;
            }
        }

    }

    public static class StringHelpers
    {
        public static List<string> BreakIntoParts(this string line, string separator)
        {
            if (String.IsNullOrEmpty(line))
                return null;
            else
            {
                return line.Split(new string[] { separator }, StringSplitOptions.None).Select(p => p.Trim()).ToList();
            }
        }
    }
}

Добавлено:

Хорошо, спасибо всем , мне больше всего нравятся ответы " return a custom class ", я никогда не думал, что out было так легко читать, мне кажется, что это взлом возвращая первую переменную в одну сторону, а вторую - , вот мой рефакторинг, возвращающий пользовательский класс :

using System;
using System.Collections.Generic;
using System.Linq;

namespace MultipleReturns
{
    class Program
    {
        static void Main(string[] args)
        {
            string extension = "txt";

            {
                IdCodeFileNamePair pair = GetIdCodeAndFileName("first.txt", extension);
                Console.WriteLine("{0}, {1}", pair.IdCode, pair.FileName);
            }

            {
                IdCodeFileNamePair pair = GetIdCodeAndFileName("first", extension);
                Console.WriteLine("{0}, {1}", pair.IdCode, pair.FileName);
            }

            Console.ReadLine();
        }

        /// <summary>
        /// gets "first.txt", "txt" and returns "first", "first.txt"
        /// gets "first", "txt" and returns "first", "first.txt"
        /// it is assumed that extensions will always match
        /// </summary>
        /// <param name="line"></param>
        public static IdCodeFileNamePair GetIdCodeAndFileName(string line, string extension)
        {
            if (line.Contains("."))
            {
                List<string> parts = line.BreakIntoParts(".");
                List<string> returnItems = new List<string>();
                return new IdCodeFileNamePair { IdCode = parts[0], FileName = line };
            }
            else
            {
                List<string> returnItems = new List<string>();
                return new IdCodeFileNamePair { IdCode = line, FileName = line + "." + extension };
            }
        }

    }

    public static class StringHelpers
    {
        public static List<string> BreakIntoParts(this string line, string separator)
        {
            if (String.IsNullOrEmpty(line))
                return null;
            else
            {
                return line.Split(new string[] { separator }, StringSplitOptions.None).Select(p => p.Trim()).ToList();
            }
        }
    }

    public class IdCodeFileNamePair
    {
        public string IdCode { get; set; }
        public string FileName { get; set; }
    }
}

Ответы [ 9 ]

12 голосов
/ 05 мая 2010

Я предпочитаю либо создать легкий класс с двумя свойствами (см. Ниже), либо использовать кортеж (теперь доступен для запекания в рамках в .NET 4, но не сложно написать свой собственный)

class MyReturnValue
{
    public string Id { get; set; }
    public string Name { get; set; }
}
8 голосов
/ 05 мая 2010

Вы можете вернуть кортеж , начиная с 4.0.

2 голосов
/ 05 мая 2010

Другой вариант - вернуть KeyValuePair<int, string>.

2 голосов
/ 05 мая 2010

Использовать ключевое слово out

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

Это способ лучше, чем приведение определенных элементов списка объектов.

1 голос
/ 05 мая 2010

Я бы порекомендовал использовать легкий объект, как предложил Марк. Но есть и другие закономерности.

Другой простой подход - использовать свойство call by reference. Мол, вызывающая сторона отправляет пустой массив в качестве параметра, который будет заполнен функцией.

1 голос
/ 05 мая 2010

Я либо использую params, либо создаю структуру со свойствами (для синтаксиса инициализатора свойства) и возвращаю это. Создание собственной структуры / класса имеет преимущество именования переменных, которое может соответствовать передаваемым данным. Это делает код более читабельным.

IdAndString GetIDAndString()
{
    return new IdAndString()
    {
        ID = 1,
        Str = "123"
    };
}

struct IdAndString
{
    public int ID { get; set; }
    public string Str { get; set; } 
}
1 голос
/ 05 мая 2010

Почему бы не public static void GetIdCodeAndFileName(string line, string extension, out string id, out string fileName)?

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

Просто не существует элегантного способа вернуть два значения в C #. Хотя я определенно думаю, что использование out параметров лучше, чем возвращение List, это не очень легко обслуживать или читать. Разработчики C # ожидают out, это поддерживается непосредственно языком, и хорошо понимает, что делает.

Начиная с 4.0, вы можете использовать Tuple.

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

Как насчет шаблона Pair<T,V>? Конечно, вы можете создать собственные типы данных для этого, если те же самые будут передаваться, но это не так универсально.

(Или, возможно, C # позволяет создавать классы на лету? update: ok игнорировать этот бит )

...