如何在C#中将接口实现委托给其他类

10
Assume the following class:
public class MyEnum: IEnumerator
{
    private List<SomeObject> _myList = new List<SomeObject>();
...
}

在MyEnum中实现IEnumerator方法是必要的。 但是否可以“委托”或直接将IEnumerator的实现重定向到_myList,而无需实现IEnumerator方法呢?

7个回答

8

方法一:继续使用封装并将调用转发到List实现。

class SomeObject
{
}

class MyEnum : IEnumerable<SomeObject>
{
    private List<SomeObject> _myList = new List<SomeObject>();

    public void Add(SomeObject o)
    {
        _myList.Add(o);
    }

    public IEnumerator<SomeObject> GetEnumerator()
    {
        return _myList.GetEnumerator();
    }

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

class Program
{
    static void Main(string[] args)
    {
        MyEnum a = new MyEnum();
        a.Add(new SomeObject());

        foreach (SomeObject o in a)
        {
            Console.WriteLine(o.GetType().ToString());
        }

        Console.ReadLine();
    }
}

方法二: 继承自List实现,您可以免费获得该行为。

class SomeObject
{
}

class MyEnum : List<SomeObject>
{
}

class Program
{
    static void Main(string[] args)
    {
        MyEnum a = new MyEnum();
        a.Add(new SomeObject());

        foreach (SomeObject o in a)
        {
            Console.WriteLine(o.GetType().ToString());
        }

        Console.ReadLine();
    }
}

方法1 可以更好地进行沙盒隔离,因为在没有MyEnum知识的情况下,不会调用List中的任何方法。对于最小的努力,首选方法2


1
如果我们能够为方法1添加一些语法糖的简写符号,比如private readonly IEnumerable<SomeObject> _myList implements IEnumerable<SomeObject>;(借鉴自Delphi的语法),那就太好了。不知道是否有人考虑过类似的东西,并且如果有,为什么还没有被添加到C#中呢? - Oliver Giesen

1
除了使用pb的方法,这是不可能的一个“简单”的原因:接口方法需要将this指针作为第一个参数传递。当您在对象上调用GetEnumerator时,此指针将是您的对象。但是,为了使嵌套列表上的调用起作用,指针必须是对该列表的引用,而不是对您的类的引用。
因此,您必须明确地将该方法委托给其他对象。
(顺便说一句,其他回复中的建议是正确的:使用IEnumerator<T>,而不是IEnumerable!)

1
你可以这样做:
public class MyEnum : IEnumerator {
    private List<SomeObject> _myList = new List<SomeObject>();
    public IEnumerator GetEnumerator() { return this._myList.GetEnumerator(); }
}

原因很简单。您的类可能包含多个集合字段,因此编译器/环境无法知道应该使用哪个字段来实现"IEnumerator"。
编辑:我同意@pb的观点-您应该实现IEnumerator 接口。

需要实现的接口是IEnumerable而不是IEnumerator。 - Jorge Ferreira

0
如果您想以一种方式返回集合,使调用者无法修改该集合,则可能需要将List包装到ReadOnlyCollection<>中,并返回ReadOnlyCollection<>的IEnumerable<>。
这样,您可以确保您的集合不会被更改。

-1
请注意,由于鸭子类型的存在,您可以在任何具有GetEnumerator方法的对象上使用foreach——对象类型实际上不需要实现IEnumerable接口。

因此,如果您这样做:

class SomeObject
{
}

 class MyEnum
 {
    private List<SomeObject> _myList = new List<SomeObject>();

    public IEnumerator<SomeObject> GetEnumerator()
    {
        return _myList.GetEnumerator();
    }
 }

那么这将非常有效:

MyEnum objects = new MyEnum();
// ... add some objects
foreach (SomeObject obj in objects)
{
    Console.WriteLine(obj.ToString());
}

我意识到这不是对原问题的一般性回答,但它与问题中提供的特定用例相关。 - yoyo
如果你意识到自己没有回答问题,那么请不要发布回答。回答是用来回答问题的,而不是发布你觉得有趣的琐碎事实。这并不能帮助你解决问题;在这种情况下,你没有理由不实际实现接口。 - Servy
问题是如何委托接口的实现,以避免编写样板代码。我的回答是针对常见情况下委托IEnumerable实现的潜在答案,这也恰好是OP使用的示例。我曾考虑过将其作为评论或答案,最终决定将其作为答案。它确实是对特定形式问题的回答。 - yoyo
然而,你在回答的全部时间里都在谈论如何没有必要实现接口,这在这种情况下既不必要也不有帮助或者是理想的。你只是解释了一个离题、无意义、不切实际的话题,而不是真正回答问题。这使得它不成为一个答案(甚至不是一个有用的评论)。 - Servy
问题询问如何轻松实现接口,而不是在没有实现“IEnumerable”的情况下如何在foreach中使用对象。 - Servy
显示剩余3条评论

-1

除非你从List<T>派生。

public class MyEnum : List<SomeObject>, IEnumerable<SomeObject>{}

好的,这个可以工作,但我想确保调用代码无法将枚举器强制转换为List,并再次添加或删除对象。 - Willem

-1

感谢大家的贡献和解释。 最终,我将你们的一些答案结合起来,得出了以下结果:

    class MyEnum : IEnumerable<SomeObject>
{
    private List<SomeObject> _myList = new List<SomeObject>();
    public IEnumerator<SomeObject> GetEnumerator()
    {
        // Create a read-only copy of the list.
        ReadOnlyCollection<CustomDevice> items = new ReadOnlyCollection<CustomDevice>(_myList);
        return items.GetEnumerator();
    }
}

这个解决方案是为了确保调用代码无法修改列表,并且每个枚举器在所有方面都是独立的(例如,排序)。 再次感谢。


您可以直接使用 ReadOnlyCollection,因为 IEnumerable 是不可变的!用户必须向上转换并猜测原始列表的类型才能修改它。复制该列表不是一个很好的选择。 - Konrad Rudolph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接