【问题标题】:Returning a reference from list of base classes returns 'nothing'从基类列表返回引用返回“无”
【发布时间】:2015-12-04 14:31:28
【问题描述】:

我有几个“项目”具有相似的功能,因此我从定义通用功能的基类中派生出它们。

之后,我还希望有这些项目的专门列表,因此我从抽象基础容器派生出类似容器的类。

以下代码演示了我的问题。

项目

public abstract class QAbstractItem 
{
    public int xAbstractMember;
    public QAbstractItem(int a)
    {
        xAbstractMember = a;
    }
}

public class QSingleItem : QAbstractItem
{
    public int xSingleMember;
    public QSingleItem(int s, int a) : base(a)
    {
        xSingleMember = s;
    }
}

列表

public abstract class QAbstractItemsList 
{
    public List<QAbstractItem> xItems = new List<QAbstractItem>();

    protected void add(QAbstractItem xItem)
    {
        xItems.Add(xItem);
    }

    public void getFirst(QAbstractItem yItem)
    {
        yItem = xItems[0];        // XXX
    }
}

public class QSingleItemsList : QAbstractItemsList
{
    public void add(QSingleItem S) 
    {
        base.add(S);
    }
}

用法

 QSingleItemsList xSingleList = new QSingleItemsList();
 xSingleList.add(new QSingleItem(5, 5));

 QSingleItem xFirst = new QSingleItem(0,0);
 xSingleList.getFirst(xFirst);

最后一行代码应该(如我所愿)在 xFirst 中包含 (5,5)。但是,它包含 (0,0)。当调试器在线XXX 时,xItems[0] 和 yFirst 都是 (5,5)。当代码返回调用者 (main) 时,xFirst 突然变为 (0,0)。为什么?

非常感谢任何帮助, 丹尼尔

【问题讨论】:

  • 您可以使用方法重载并通过 ref 传递参数。例如,一个重载是public void getFirst(ref QSingleItem yItem)。但您仍然必须在重载方法中进行强制转换。但我认为它更好。 yItem = (QAbstractItem)xItems[0];

标签: c# abstract-class derived-class


【解决方案1】:
public void getFirst(QAbstractItem yItem)
{
    yItem = xItems[0];        // XXX
}

yItem 参数是按值传递的(即使它是引用类型;引用是按值传递的)。所以当你给 yItem 赋值时,调用者不受影响,因为yItem 只包含原始引用的副本。为此,您需要通过引用传递yItem

public void getFirst(ref QAbstractItem yItem)
{
    yItem = xItems[0];        // XXX
}

但是,在这种情况下,您将无法使用 QSingleItem 类型的参数调用它。


您的问题的另一种解决方案是使列表类通用:

public class QItemsList<TItem> where TItem : QAbstractItem
{
    public List<TItem> xItems = new List<TItem>();

    protected void add(TItem xItem)
    {
        xItems.Add(xItem);
    }

    public void getFirst(out TItem yItem)
    {
        yItem = xItems[0];        // XXX
    }
}

如果您需要为给定类型的项目提供专门的方法,您可以将它们添加到继承通用列表的类中:

public class QSingleItemsList : QItemsList<QSingleItem>
{
    // specialized methods here
}

【讨论】:

  • 感谢您的澄清。因此,有没有一种方法可以创建从 abstractItems 的抽象列表派生的 singleItems 列表?我感觉这肯定已经做了一千次了,但是我不知道如何搜索它。
  • @DanielBencik,抽象列表在您的场景中的价值是什么?为什么不直接使用List&lt;QSingleItem&gt;
  • 我想知道为什么它是按值传递的。我认为类总是默认通过引用传递。这是因为抽象吗?
  • @Thomas Levesque:这种情况是有更多的派生类,如 QSingleItem,因此有不止一个专门的列表。这就是为什么将常用功能卸载到抽象库是值得的。
  • @M.kazemAkhgary,与抽象的事实无关。 C# 中的参数(即使是引用类型)总是按值,除非另有说明;但在引用类型的情况下,它是按值传递的引用。阅读这篇文章以获得更详细的解释:yoda.arachsys.com/csharp/parameters.html
【解决方案2】:

您对getFirst 的实现不正确。您需要向其中添加 ref 或 out 关键字。 ref 关键字表示传入的值可以重新赋值,out 关键字表示忽略输入值并将结果赋值给那里。

public void getFirst(ref QAbstractItem yItem)

public void getFirst(out QAbstractItem yItem)

然后它会被这样调用

QAbstractItem xFirst = new QSingleItem(0,0);
xSingleList.getFirst(ref xFirst);

让它工作,但实际上它应该更接近

public QAbstractItem getFirst()

【讨论】:

  • ref/out 的问题在于它不适用于抽象类,正如 Thomas Levesque 也指出的那样。至于实际应用中的public QAbstractItem getFirst(),我需要得到一对特定的QSingleItems,因此我在参数中“返回”。
  • @DanielBencik 返回一个包含这对对象的对象可能比使用out/ref 更有意义。
  • @DanielBencik 你已经失去了我,getFirst,只给你一件事。你的意思是你想让它们成为QSingItem而不是QAbstractItem
  • @Sign:是的,getFirst 是为演示目的而编写的。实际上,我想要getSomeTwo() 之类的东西,即我需要返回一对特别选择的 QSingleItems。\
  • @DanielBencik 那么为什么不返回 Tuple&lt;QAbstractitem, QAbstractItem&gt; 呢?还是包含两者的其他更具体的东西?
【解决方案3】:

问题出在那个方法上:

public void getFirst(QAbstractItem yItem)
{
    yItem = xItems[0];        // XXX
}

您必须注意,当您将参数传递给方法时,会创建对 yItem 的引用的副本。因此,如果您为yItem 分配新值,它不会影响您传递给方法getFirst 的实际参数。 根据 C# 规范,这是正确的行为。

您可以考虑更改方法以返回第一个元素:

public QAbstractItem getFirst()
{
    return xItems[0];        // XXX
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 2018-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多