出参必须在控制流离开当前方法之前被赋值。

4

我对C#非常陌生,这是我第一次使用列表,所以我的问题可能很愚蠢... 我试图从文件中读取数据到一个由Tourist对象组成的列表中。据我所知,在添加对象之前需要为tourists列表分配一些内容,但我不确定如何做到这一点。

class Tourist
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public double Contributed { get; set; }

    public Tourist(string firstName, string lastName, double money)
    {
        FirstName = firstName;
        LastName = lastName;
        Contributed = money * 0.25;
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Tourist> tourists = new List<Tourist>();

        ReadData(out tourists);
    }

    static void ReadData(out List<Tourist> tourists)
    {
        const string Input = "..\\..\\Duomenys.txt";

        string[] lines = File.ReadAllLines(Input);
        foreach (string line in lines)
        {
            string[] values = line.Split(';');
            string firstName = values[0];
            string lastName = values[1];
            double money = Double.Parse(values[2]);
            tourists.Add(new Tourist(firstName, lastName, money));
        }
    }
}

9
不需要使用out参数。一个应该读取数据并返回它的方法不应该是void,而应该返回List<Tourist>。然而,out参数必须在方法内部初始化,这就是编译器抱怨的原因。out参数像是方法和调用方之间的契约,可以保证该参数在方法中被分配和初始化(尽管后者实际上可能会被赋值为null)。 - Tim Schmelter
3个回答

5
通过将参数声明为out,您“承诺”调用者(和编译器)您的方法将为该参数提供的变量“设置一个值”。因为您做出了这个承诺,您的方法中的每个路径都必须将一个值分配给此参数。您的方法没有为tourists分配一个值。如果使用null引用调用该方法,则可能导致tourists.Add(...)引发NullReferenceException异常。在我的看来,由于您已经在Main中初始化了tourists,所以似乎可以省略out关键字。请注意,ReadData仅修改列表的内容,而不是存储在tourists变量中的引用。由于您不想更改该引用(变量的值),因此不需要out关键字。如果您希望ReadData对其进行初始化,则需要添加以下行:
tourists = new List<Tourist>()

foreach循环之前,在ReadData中进行操作。


按照您的代码,更好的解决方案是不传递任何参数给ReadData方法,并让该方法返回列表:

static List<Tourist> ReadData()
{
    // create list
    List<Tourist> tourists = new List<Tourist>();

    const string Input = "..\\..\\Duomenys.txt";       

    string[] lines = File.ReadAllLines(Input);
    foreach (string line in lines)
    {
        // shortened for brevity
        tourists.Add(new Tourist(firstName, lastName, money));
    }

    return tourists; // return newly created list
}

并在 Main 中使用如下:

static void Main(string[] args)
{
    List<Tourist> tourists = ReadData();
}

2

带有out参数的需要在方法体内进行赋值,而不是从调用方传递参数。引用msdn的说法:

在方法中,与本地变量一样,输出参数最初被认为是未分配的,在使用其值之前必须明确分配。

针对您的情况,有三种解决方法:

首先,您可以将赋值从调用者移到方法中:

static void Main(string[] args)
{
    List<Tourist> tourists;
    ReadData(out tourists);
}

static void ReadData(out List<Tourist> tourists)
{
    tourists = new List<Tourist>();
    //...
}

第二种方法是你可以修改声明,跳过在声明中的out,这将把初始值传递到方法并且免除在该方法中进行分配的要求。

最后一个选项(在可读性方面我认为是最好的)是将列表作为返回值而不是out参数:

static void Main(string[] args)
{
    List<Tourist> tourists = ReadData();
}

static List<Tourist> ReadData()
{
    List<Tourist> tourists = new List<Tourist>();
    //...
    return tourists;
}

1

在离开方法之前,您必须为out参数提供一个值。

static void ReadData(out List<Tourist> tourists)
{
    const string Input = "..\\..\\Duomenys.txt";
    tourists = new List<Tourist>();

    string[] lines = File.ReadAllLines(Input);
    foreach (string line in lines)
    {
        string[] values = line.Split(';');
        string firstName = values[0];
        string lastName = values[1];
        double money = Double.Parse(values[2]);
        tourists.Add(new Tourist(firstName, lastName, money));
    }
}

然而,由于您传递的是引用类型的List<T>,因此不需要使用out关键字。因为您所引用的对象的修改会在所有引用中反映出来。
因此,在调用ReadData之后,您提供的列表也将发生更改:
var tourists = new List<Tourist>();
ReadData(toursists);  // this changes the elements within the list
// do something with the list

不仅在这种情况下,而是在每种情况下,“out”参数在方法结束之前必须设置。 - kiziu
@kiziu 你说得没错,但这个主题只有两条执行路径:一条执行循环,另一条不执行,因此我的评论无论如何都是正确的。 - MakePeaceGreatAgain
1
虽然这是正确的,但我认为你回答的第一句话可能会误导人。 "如果" 可能会导致这样的结论:如果循环执行,则不需要 out 的值,这是不正确的,因为循环不会初始化参数,它只会修改它。 - kiziu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接