繁体   English   中英

你如何归还收藏品 <ConcreteType> 作为一个集合 <Interface> ?

[英]How can you return a Collection<ConcreteType> as a Collection<Interface>?

我有一个具体的类,其中包含另一个具体类的集合。 我想通过接口公开这两个类,但是我无法弄清楚如何将Collection <ConcreteType>成员公开为Collection <Interface>成员。

我目前正在使用.NET 2.0

下面的代码导致编译器错误:

无法将类型'System.Collections.ObjectModel.Collection <Nail>'隐式转换为'System.Collections.ObjectModel.Collection <INail>'

注释的演绎尝试给出了这个编译错误:

无法将类型'System.Collections.ObjectModel.Collection <Nail>'转换为
'System.Collections.ObjectModel.Collection <INail>'通过引用转换,装箱转换,拆箱转换,换行转换或空类型转换。

有没有办法将具体类型的集合公开为接口集合,还是需要在接口的getter方法中创建新集合?

using System.Collections.ObjectModel;

public interface IBucket
{
    Collection<INail> Nails
    {
        get;
    }
}

public interface INail
{
}

internal sealed class Nail : INail
{
}

internal sealed class Bucket : IBucket
{
    private Collection<Nail> nails;

    Collection<INail> IBucket.Nails
    {
        get
        {
            //return (nails as Collection<INail>);
            return nails;
        }
    }

    public Bucket()
    {
        this.nails = new Collection<Nail>();
    }
}

C#3.0泛型是不变的。 如果不创建新对象,则无法做到这一点。 C#4.0引入了安全的协方差/逆变,无论如何都不会改变读/写集合(你的情况)。

只需将指甲定义为

Collection<INail>

为什么不将它作为一个接口返回,只需在接口中拥有所有公共方法,这样你就不会遇到这个问题,如果你以后决定返回另一种类型的Nail类,那么它就可以了。

您使用的是什么版本的.Net?

如果您使用的是.net 3.0+,则只能使用System.Linq来实现此目的。

看看这个问题这个问题解决了我。

有一种解决方案可能不是您要求的,但可能是一种可接受的替代方案 - 而是使用数组。

internal sealed class Bucket : IBucket
{
    private Nail[] nails;

    INail[] IBucket.Nails
    {
        get { return this.nails; }
    }

    public Bucket()
    {
        this.nails = new Nail[100];
    }
}

(如果你最终做这样的事情,请记住这个框架设计指南说明:通常不应将数组作为属性公开,因为它们通常在返回给调用者之前被复制,并且复制是一个昂贵的操作无辜的财产得到。)

使用它作为你的属性getter的主体:

List<INail> tempNails = new List<INail>();
foreach (Nail nail in nails)
{
    tempNails.Add(nail);
}
ReadOnlyCollection<INail> readOnlyTempNails = new ReadOnlyCollection<INail>(tempNails);
return readOnlyTempNails;

这有点像一个hacky解决方案,但它做你想要的。

编辑返回ReadOnlyCollection。 确保在IBucket和Bucket中更新您的类型。

您可以添加一些泛型。 更好,更强耦合。

public interface IBucket<T> where T : INail
{
    Collection<T> Nails
    {
        get;
    }
}

public interface INail
{
}

internal sealed class Nail : INail
{
}

internal sealed class Bucket : IBucket<Nail>
{
    private Collection<Nail> nails;

    Collection<Nail> IBucket<Nail>.Nails
    {
        get
        {
            return nails; //works
        }
    }

    public Bucket()
    {
        this.nails = new Collection<Nail>();
    }
}

这样你从Bucket类返回的Collection<Nail>只能持有Nail 任何其他INail不会进入它。 根据您的需要,这可能会或可能不会更好。


只有当你想要从Bucket返回的Collection<INail> (接口属性)来保存其他INail (比Nail s)时,你可以尝试以下方法。 但有一个问题。 一方面你说你想在Bucket类中使用private Collection<Nail>而不是Collection<INail>因为你不想意外地将Bucket类中的其他INail添加到它中,但另一方面你将不得不添加来自Bucket课外的其他INail 在同一个实例上是不可能 编译器阻止您意外地将任何INail添加到Collection<Nail> 一种方法是从现有Collection<Nail> Bucket类返回Collection<INail>的不同实例。 这效率较低,但可能是您所追求的语义。 注意,这在概念上与上面不同

internal sealed class Bucket : IBucket
{
    private Collection<Nail> nails;

    Collection<INail> IBucket<Nail>.Nails
    {
        get
        {
            List<INail> temp = new List<INail>();
            foreach (Nail nail in nails)
                temp.Add(nail);

            return new Collection<INail>(temp);  
        }
    }

    public Bucket()
    {
        this.nails = new Collection<Nail>();
    }
}

你可以使用Cast扩展

nails.Cast<INail>()

我不能在这里测试它提供一个更全面的例子,因为我们在工作中使用.NET 2.0(抱抱抱怨),但我在这里确实有类似的问题

C#不支持通用集合协方差(它仅支持数组)。 在这种情况下我使用适配器类。 它只是将所有调用重定向到实际集合,将值转换为所需类型(不需要将所有列表值复制到新集合)。 用法如下:

Collection<INail> IBucket.Nails
{
    get
    {
        return new ListAdapter<Nail, INail>(nails);
    }
}

    // my implementation (it's incomplete)
    public class ListAdapter<T_Src, T_Dst> : IList<T_Dst>
{
    public ListAdapter(IList<T_Src> val)
    {
        _vals = val;
    }

    IList<T_Src> _vals;

    protected static T_Src ConvertToSrc(T_Dst val)
    {
        return (T_Src)((object)val);
    }

    protected static T_Dst ConvertToDst(T_Src val)
    {
        return (T_Dst)((object)val);
    }

    public void Add(T_Dst item)
    {
        T_Src val = ConvertToSrc(item);
        _vals.Add(val);
    }

    public void Clear()
    {
        _vals.Clear();
    }

    public bool Contains(T_Dst item)
    {
        return _vals.Contains(ConvertToSrc(item));
    }

    public void CopyTo(T_Dst[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public int Count
    {
        get { return _vals.Count; }
    }

    public bool IsReadOnly
    {
        get { return _vals.IsReadOnly; }
    }

    public bool Remove(T_Dst item)
    {
        return _vals.Remove(ConvertToSrc(item));
    }

    public IEnumerator<T_Dst> GetEnumerator()
    {
        foreach (T_Src cur in _vals)
            yield return ConvertToDst(cur);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public override string ToString()
    {
        return string.Format("Count = {0}", _vals.Count);
    }

    public int IndexOf(T_Dst item)
    {
        return _vals.IndexOf(ConvertToSrc(item));
    }

    public void Insert(int index, T_Dst item)
    {
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }

    public T_Dst this[int index]
    {
        get { return ConvertToDst(_vals[index]); }
        set { _vals[index] = ConvertToSrc(value); }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM