[英]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类,那么它就可以了。
有一种解决方案可能不是您要求的,但可能是一种可接受的替代方案 - 而是使用数组。
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>();
}
}
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.