如何返回 Collection作为集合<接口>?

发布于 2024-08-02 20:40:57 字数 1247 浏览 9 评论 0原文

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

我当前正在使用 .NET 2.0

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

无法隐式转换类型 'System.Collections.ObjectModel.Collection''到 'System.Collections.ObjectModel.Collection'

注释的尝试强制转换给出了此编译器错误:

无法转换类型 'System.Collections.ObjectModel.Collection''到
'System.Collections.ObjectModel.Collection'通过一个 引用转换、装箱转换、拆箱转换、包装 转换,或 null 类型转换。

有没有办法将具体类型的集合公开为接口集合,或者我是否需要在接口的 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>();
    }
}

I have a concrete class that contains a collection of another concrete class. I would like to expose both classes via interfaces, but I am having trouble figuring out how I can expose the Collection<ConcreteType> member as a Collection<Interface> member.

I am currently using .NET 2.0

The code below results in a compiler error:

Cannot implicitly convert type
'System.Collections.ObjectModel.Collection<Nail>' to
'System.Collections.ObjectModel.Collection<INail>'

The commented attempt to cast give this compiler error:

Cannot convert type
'System.Collections.ObjectModel.Collection<Nail>' to
'System.Collections.ObjectModel.Collection<INail>' via a
reference conversion, boxing conversion, unboxing conversion, wrapping
conversion, or null type conversion.

Is there any way to expose the collection of concrete types as a collection of interfaces or do I need to create a new collection in the getter method of the interface?

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>();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

失退 2024-08-09 20:40:57

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

C# 3.0 generics are invariant. You can't do that without creating a new object. C# 4.0 introduces safe covariance/contravariance which won't change anything about read/write collections (your case) anyway.

森林迷了鹿 2024-08-09 20:40:57

只需将指甲定义为

Collection<INail>

Just define nails as

Collection<INail>
不美如何 2024-08-09 20:40:57

为什么不只是将其作为接口返回,只需将所有公共方法都放在接口中,这样就不会出现此问题,而且,如果您稍后决定返回另一种类型的 Nail 类,那么它会正常工作。

Why not just return it as an interface, just have all your public methods in the interface, that way you don't have this problem, and, if you later decide to return another type of Nail class then it would work fine.

可是我不能没有你 2024-08-09 20:40:57

您使用什么版本的.Net?

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

查看这个问题,它为我解决了这个问题。

What version of .Net are you using?

If you are using .net 3.0+, you can only achieve this by using System.Linq.

Check out this question, which solved it for me.

双手揣兜 2024-08-09 20:40:57

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

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

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

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

(如果您最终执行了类似的操作,请记住此框架设计指南注释:通常不应将数组公开为属性,因为它们通常在返回给调用者之前进行复制,并且在数组内部执行复制是一项昂贵的操作无辜财产得到。)

There is one solution that might not be quite what you are asking for but could be an acceptable alternative -- use arrays instead.

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

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

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

(If you end up doing something like this, keep in mind this Framework Design Guidelines note: generally arrays shouldn't be exposed as properties, since they are typically copied before being returned to the caller and copying is an expensive operation to do inside an innocent property get.)

东北女汉子 2024-08-09 20:40:57

使用它作为你的属性获取器的主体:

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

这是一个有点老套的解决方案,但它可以满足你的要求。

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

use this as the body of your property getter:

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

That is a tad bit of a hacky solution but it does what you want.

Edited to return a ReadOnlyCollection. Make sure to update your types in IBucket and Bucket.

缘字诀 2024-08-09 20:40:57

您可以添加一些泛型。更适合,耦合更紧密。

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。任何其他 Iail 都不会进入其中。这可能会更好,也可能不会更好,具体取决于您想要什么。


仅当您希望从 Bucket 返回的 Collection(接口属性)来保存其他 INail(而非 Nail< /code>s) 那么你可以尝试下面的方法。但有一个问题。一方面,您说您想在 Bucket 类中使用 private Collection 而不是 Collection 因为您不希望意外地将 Bucket 类中的其他 INail 添加到其中,但另一方面,您必须从 外部添加其他 INail >Bucket 类。 在同一个实例上这是不可能的。编译器会阻止您意外地将任何 INail 添加到 Collection 中。一种方法是从现有 CollectionBucket 类返回不同的 Collection 实例。这效率较低,但可能就是您所追求的语义。 请注意,这在概念上与上面不同

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>();
    }
}

You can add some generics. Fits better, more strongly coupled.

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>();
    }
}

This way the Collection<Nail> you return from Bucket class can only ever hold Nails. Any other INail wont go into it. This may or may not be better depending on what you want.


Only if you want Collection<INail> (the interface property) you return from Bucket to hold other INails (than Nails) then you may try the below approach. But there is a problem. On one side you say you want to use a private Collection<Nail> in Bucket class and not a Collection<INail> because you dont want to accidentally add other INails from Bucket class into it but on the other side you will have to add other INails from outside of Bucket class. This is not possible on the same instance. Compiler stops you from accidentally adding any INail to a Collection<Nail>. One way is to return a different instance of Collection<INail> from your Bucket class from the existing Collection<Nail>. This is less efficient, but could be the semantics you are after. Note that this is conceptually different from above

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>();
    }
}
儭儭莪哋寶赑 2024-08-09 20:40:57

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); }
    }
}

C# doesn't support generic collections covariance (it's only supported for arrays).
I use an adapter class in such cases. It just redirects all calls to the actual collection, converting values to the required type (doesn't require copying all list values to the new collection).
Usage looks like this:

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); }
    }
}
葮薆情 2024-08-09 20:40:57

你可以使用 Cast 扩展,

nails.Cast<INail>()

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

you could use the Cast extension

nails.Cast<INail>()

I can't test it here to provide a more comprehensive example, as we are using .NET 2.0 at work (gripe gripe), but I did have a similar question here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文