【问题标题】:How to pass List<DerivedClass> when param type is List<BaseClass>?当参数类型为 List<BaseClass> 时如何传递 List<DerivedClass>?
【发布时间】:2011-11-30 09:19:44
【问题描述】:

我如何传递一个列表,该列表是一个 DerivedObjects 列表,其中该方法需要一个 BaseObjects 列表。我正在转换列表.ToList&lt;BaseClass&gt;() 并想知道是否有更好的方法。我的第二个问题是语法不正确。我正在尝试通过引用传递列表,但出现错误:'ref' argument is not classified as a variable

如何解决这两个问题?谢谢。

public class BaseClass { }
public class DerivedClass : BaseClass { }

class Program
{
    static void Main(string[] args)
    {
        List<DerivedClass> myDerivedList = new List<DerivedClass>();
        PassList(ref myDerivedList.ToList<BaseClass>());
// SYNTAX ERROR ABOVE IS - 'ref' argument is not classified as a variable

        Console.WriteLine(myDerivedList.Count);
    }

    public static void PassList(ref List<BaseClass> myList)
    {
        myList.Add(new DerivedClass());
        Console.WriteLine(myList.Count);
    }
}

已解决:

类似的方法解决了我的问题。

public static void PassList<T>(ref List<T> myList) where T : BaseClass
{
    if (myList == null) myList = new List<T>(); 
    // sorry, i know i left this out of the above example.

    var x = Activator.CreateInstance(typeof(T), new object[] {}) as T;
    myList.Add(x);
    Console.WriteLine(myList.Count);
}

感谢所有帮助解决这个问题和其他 SO 问题的人。

【问题讨论】:

  • 在这种情况下为什么需要ref
  • ref 是必需的,否则添加的项目仍然与该方法一起装箱。 (希望我得到了正确的条款)。
  • List 一个引用类型类。方法中添加的项目将被添加到同一个实例中。
  • @Valamas:不,你不明白ref 是如何工作的(或者可能是引用类型是如何工作的)。按照我的答案中的链接并非常仔细地阅读它
  • 我以前没有见过“where T : BaseClass”语法,这很有用。感谢您发布已解决的代码。

标签: c# derived-class generic-list ref


【解决方案1】:

ref 部分很简单:通过引用传递参数,基本上它必须是一个变量。所以你可以写:

List<BaseClass> tmp = myDerivedList.ToList<BaseClass>();
PassList(ref tmp);

...但这不会影响myDerivedList的值或内容本身。

无论如何,这里的ref 毫无意义,因为无论如何您都不会在方法中更改myList 的值。了解更改参数值与更改参数值所指对象的内容之间的区别很重要。详情请见my article on parameter passing

现在至于为什么你不能传递你的列表 - 这是为了保护类型安全。假设你可以这样做,我们可以这样写:

List<OtherDerivedClass> list = new List<OtherDerivedClass>();
PassList(list);

现在这将尝试将DerivedClass 的实例添加到List&lt;OtherDerivedClass&gt; - 这就像在一堆香蕉中添加一个苹果......它不起作用! C# 编译器会阻止您执行不安全的操作 - 它不会让您将一堆香蕉视为水果盘。假设我们确实Fruit 而不是BaseClass,和Banana / Apple 作为两个派生类,PassListApple 添加到它给出的列表中:

// This is fine - you can add an apple to a fruit bowl with no problems
List<Fruit> fruitBowl = new List<Fruit>();
PassList(fruitBowl);

// This wouldn't compile because the compiler doesn't "know" that in PassList
// you're only actually adding an apple.
List<Apple> bagOfApples = new List<Apple>();
PassList(bagOfApples);    

// This is the dangerous situation, where you'd be trying to really violate
// type safety, inserting a non-Banana into a bunch of bananas. But the compiler
// can't tell the difference between this and the previous one, based only on
// the fact that you're trying to convert a List<Banana or Apple> to List<Fruit>
List<Banana> bunchOfBananas = new List<Banana>();
PassList(bunchOfBananas );    

C# 4 在某些情况下允许 泛型变量 - 但在这种特殊情况下它无济于事,因为您正在做一些根本不安全的事情。不过,通用方差是一个相当复杂的话题 - 由于您仍在学习参数传递的工作原理,我强烈建议您暂时不要管它,直到您对其余部分更有信心语言。

【讨论】:

  • 为 Jon 的解释和指向您网站的链接干杯。我已经使用泛型解决了我的问题。
  • 我能问一下List&lt;BaseClass&gt; tmp = myDerivedList.ToList&lt;BaseClass&gt;(); 到底是做什么的吗?它是创建副本还是什么?我有类似的需求,而且——令我惊讶的是——如果我这样做并将结果传递给我的方法,对对象所做的更改会反映在原始列表中。 (我正在修改对象,而不是在列表中添加或删除任何内容。)
  • @JonathanWood:它创建了列表的浅表副本。因此,在添加和删除方面对原始列表的更改将在副本中显示,但对对象的更改会 - 因为新列表包含对相同对象的引用。
【解决方案2】:

如果您必须保留ref,您可以这样做:

var list = myDerivedList.ToList<BaseClass>();
PassList(ref list);
// SYNTAX ERROR ABOVE IS - 'ref' argument is not classified as a variable

对于ref 参数,它需要一个变量来引用。即时创建时,后面的代码中没有可引用的内容,所以它真的没有意义。

【讨论】:

    【解决方案3】:

    首先,在您提供的代码中,您不需要ref,因为您并没有真正以任何方式更改destination(参考)myList。 然后你当然会得到一个错误 ref myDerivedList.ToList&lt;..&gt;() 因为使用 byref 你可以改变一个变量指向的位置,但是你不给一个变量,你给一个 value (所以只需阅读编译器抱怨;))

    给你点:阅读Co- and Contravariance - 如果你使用.net 4.0

    【讨论】:

      猜你喜欢
      • 2010-12-21
      • 1970-01-01
      • 2012-10-17
      • 1970-01-01
      • 2013-06-02
      相关资源
      最近更新 更多