Как делегировать реализацию интерфейса другому классу В C#
предположим следующий класс:
public class MyEnum: IEnumerator
{
private List<SomeObject> _myList = new List<SomeObject>();
...
}
необходимо реализовать методы IEnumerator в MyEnum. Но можно ли "делегировать" или перенаправить реализацию для IEnumerator непосредственно в _myList без необходимости реализации методов IEnumerator?
7 ответов
Способ 1: Продолжайте использовать инкапсуляцию и пересылать вызовы реализации списка.
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();
}
}
Способ 2: Наследование от реализации списка вы получаете это поведение бесплатно.
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. Для наименьших усилий Способ 2 предпочтительнее.
вы можете сделать это:
public class MyEnum : IEnumerator {
private List<SomeObject> _myList = new List<SomeObject>();
public IEnumerator GetEnumerator() { return this._myList.GetEnumerator(); }
}
причина проста. Ваш класс может содержать несколько полей, которые являются коллекциями, поэтому компилятор/среда не могут знать, какое поле следует использовать для реализации "IEnumerator".
EIDT: Я согласен с @pb-вы должны реализовать интерфейс IEnumerator
Помимо использования метода pb, это невозможно по" простой " причине: метод интерфейса должен пройти this
указатель в качестве первого аргумента. Когда вы звоните GetEnumerator
на вашем объекте этот указатель будет вашим объектом. Однако, чтобы вызов работал над вложенным списком, указатель должен быть ссылкой на этот список, а не на ваш класс.
поэтому вы явно должны делегировать метод другому объекту.
(и кстати, совет в другом ответе был прав: используйте IEnumerator<T>
, не IEnumerable
!)
Если вы хотите вернуть коллекцию таким образом, чтобы вызывающий не смог изменить коллекцию, вы можете обернуть список в ReadOnlyCollection и вернуть IEnumerable из ReadOnlyCollection.
таким образом, вы можете быть уверены, что ваша коллекция не будет изменен.
нет, если вы не производны от List
public class MyEnum : List<SomeObject>, IEnumerable<SomeObject>{}
спасибо всем за ваши ответы и объяснения. В конце концов, я объединил некоторые из ваших ответов на следующие:
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();
}
}
это решение должно гарантировать, что вызывающий код неспособен изменять список, и каждый перечислитель независим от других во всех отношениях (например, с сортировкой). Спасибо снова.
обратите внимание, что благодаря утка-типирование, вы можете использовать foreach
на любом объекте, который имеет GetEnumerator
способ - тип объекта, на самом деле не нужно выполнять 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());
}