投射列表<a> to List<b> without making a new copy</b></a>

时间:2013-03-24 22:46:37

标签: c# casting

请注意以下代码:

public class MyClass
{
    List<object> _List = new List<object>();

    public List<object> Objects { get { return _List; } }
    public List<string> Strings { get { return _List.Cast<string>().ToList(); } }

    public void Test()
    {
        Objects.Add ("Hello");
        // actual contents
        // Objects = object[] { "Hello" }
        // Strings = string[] { "Hello" }

        Strings.Add ("World");
        // actual contents
        // Objects = object[] { "Hello" }
        // Strings = string[] { "Hello" }

        // expected contents
        // Objects = object[] { "Hello", "World" }
        // Strings = string[] { "Hello", "World" }
    }
}

代码显示了一个包含对象列表的类。这两个属性分别将该列表公开为List<object>List<string>。但是,由于_List.Cast<string>().ToList()创建了实际列表的COPY,因此行Strings.Add ("World")不会影响实际列表。有没有办法确保在Strings属性中返回实际列表的CASTED REFERENCE而不是实际列表的CASTED COPY?

注意:当一个整数添加到Objects然后从Strings访问时,代码可能会失败,但这不是我现在担心的。

修改

原始问题如下。它被改变以避免混淆并简化手头的问题。请注意以下代码:

public abstract class Foo
{
    List<object> _List = new List<object>();
    public List<object> ListObject { get { return _List; } }
}
public class Bar : Foo
{
    public List<string> ListString
    {
        get { return ListObject.Cast<string>().ToList(); }
    }
}

Bar oBar = new Bar();
Foo oFoo = oBar;

oFoo.ListObject.Add("Item");
// oFoo.ListObject= { "Item" }
// oBar.ListString = { "Item" }

oBar.ListString.Add("NewItem");
// oFoo.ListObject= { "Item" }
// oBar.ListString = { "Item" }

正如您所看到的,使用基类对象可以正常工作(项目被添加到内部列表中),但使用派生类对象不起作用。我知道原因是因为将List转换为List实际上会创建一个列表的新副本并返回该列表。我想知道是否可以编写这些类,以便它可以双向工作。

4 个答案:

答案 0 :(得分:2)

您可以尝试这样的事情:

public abstract class Foo<T>
{
    List<T> _List = new List<T>();
    public List<T> ListObject { get { return _List; } }
}
public class Bar : Foo<string>
{
    public List<string> ListString
    {
        get { return ListObject; }
    }
}

结果: enter image description here

答案 1 :(得分:2)

我希望其他人会对此提出一个相当不错的答案,但现实情况是,这个答案可能没有好的答案。

然而,有几种方法可以给你的普通猫提供皮肤,其中很多都非常难看。

这个问题的一个丑陋的解决方案是实现一个封装n List<object>对象的列表类,并尝试以您选择的任何类型访问对象。这种类型的代理类可能很难做到正确,但可能是你想要做的事情的一种方式。

public class StringObjectList : IList<string>
{
    private List<object> _list;
    public StringObjectList(List<object> src)
    {
        _list = src;
    }

    // IList Implementation...

    public string this[int index]
    {
        get
        {
            object obj = _list[index];
            if (obj == null)
                return null;
            return obj.ToString();
        }
        set
        {
            _list[index] = value;
        }
    }

    // ... plus 3 more IList<string> methods (IndexOf, Insert, RemoveAt)

    // ICollection<string> implementation (5 methods, 2 properties)

    // IEnumerable<string> implementation (1 method)

    // IEnumerable implementation (1 method)
}

一些实施细节有点棘手。虽然实现是简单的代理方法,但主要是因为底层列表很乐意接受字符串以及任何其他对象。例如ICollection<string>.Add方法可以简单如下:

public void Add(string item)
{
    _list.Add(item);
}

您可能遇到问题的地方是IEnumerable<string>IEnumerable实施,这可能需要您创建一些支持类。

不简单,不优雅,但可能是可行的。

答案 2 :(得分:0)

如果您不喜欢上面的通用解决方案,可以将List成员抽象化。

public abstract class Foo
{
    public abstract IList ListObject { get; }
}

public class Bar : Foo
{
    public override IList ListObject
    {
        get { return new List<string>(); }
    }
}

答案 3 :(得分:0)

    public abstract class Foo<T>
    {
        public abstract IList<T> MyList { get; }
        // you can manipulate MyList in this class even if it is defined in inherited class
    }

    public class Bar : Foo<string>
    {
        private readonly IList<string> _myList = new List<string>();

        public override IList<string> MyList
        {
            get { return _myList; }
        }
    }

    [TestFixture]
    public class TestFixture1
    {
        [Test]
        public void Test()
        {
            Bar oBar = new Bar();
            Foo<string> oFoo = oBar;

            oFoo.MyList.Add("Item");
            // oFoo.ListObject= { "Item" }
            // oBar.ListString = { "Item" }

            oBar.MyList.Add("NewItem");
            // oFoo.ListObject= { "Item" }
            // oBar.ListString = { "Item" }
        }

    }