开发者

What is the best way to return two values from a method?

When I have to write methods which return two values, I usually go about it as in the following code which returns a List<string>. Or if I have to return e.g. a id and string, then I return a List<object> and then pick them out with index number and recast the values.

This recasting and referencing by index seems inelegant so I want to develop a new habit for methods that return two values. What is the best pattern for this?

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();
            }
        }
    }
}

Added:

Ok, thanks everyone, I like the "return a custom class" answers best, never really thought out was that easy to read, seems like a hack to me returning the first variable one way and the second another, here is my refactoring returning a custom class:

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 => 开发者_如何学Cp.Trim()).ToList();
            }
        }
    }

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


I prefer either to create a lightweight class with two properties (see below), or to use a tuple (now available baked into the framework in .NET 4 but not hard to write your own)

class MyReturnValue
{
    public string Id { get; set; }
    public string Name { get; set; }
}


You could return a tuple, starting with 4.0.


Use keyword out

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

This is way better than casting specific elements of a list of objects.


Another option is to return a KeyValuePair<int, string>.


Why not public static void GetIdCodeAndFileName(string line, string extension, out string id, out string fileName)?


I either use out params, or create a struct with properties (for the property initializer syntax) and return that. Creating a custom struct/class has the advantage of variable naming that can match the data being passed. This makes the code more readable.

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

struct IdAndString
{
    public int ID { get; set; }
    public string Str { get; set; } 
}


I would recommend using of a Light weight object like Mark suggested. But there are other patterns as well.

Another simple approach is, use the call by reference property. Like, the caller sends an empty array as a parameter, which will be populated by the function.


How about a template Pair<T,V>? Of course you can construct custom data-types for this if the same ones will be passed around, but it's not as generic.

(Or perhaps does C# let you generate classes on-the-fly? update: ok ignore this bit)


There just plain isn't an elegant way to return two values in C#. Although I definitely think using out parameters is better than returning a List, that's not very maintainable or readable. C# developers expect out, it's supported directly by the language, and it's well understood what it's doing.

Starting in 4.0, you can use a Tuple instead.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜