Подтвердить что ты не робот

Класс с несколькими IEnumerable <T> интерфейсами на нем - Что делать с не-общим методом?

Думаю, я понимаю, почему IEnumerable<T> наследует от IEnumerable после прочтения сообщения: Почему IEnumerable<T> наследуется от IEnumerable?

Однако я не уверен, как лучше всего реализовать не-общий метод при использовании 2-х общих интерфейсов? Вот пример кода, который я пишу:

public interface IComponentA { /* ... Interface A Code ... */ }

public interface IComponentB { /* ... Interface B Code ... */ }

public class ComponentModel: IEnumerable<IComponentA>, IEnumerable<IComponentB>
{
    public ComponentModel() { }

    private List<IComponentA> ListOfComponentA = new List<IComponentA>();
    private List<IComponentB> ListOfComponentB = new List<IComponentB>();

    // ... Some public methods to add and remove components (for A and B).

    IEnumerator<IComponentA> IEnumerable<IComponentA>.GetEnumerator()
    {
        return ListOfComponentA.GetEnumerator();
    }

    IEnumerator<IComponentB> IEnumerable<IComponentB>.GetEnumerator()
    {
        return ListOfComponentB.GetEnumerator();
    }

    // The fact that IEnumerable<T> inherits from the non-generic IEnumerable
    // now means I have to deal with this.
    IEnumerator IEnumerable.GetEnumerator()
    {
        // Throwing a NotImplementedException is probably not a good idea
        // so what should I put in here?
        throw new NotImplementedException();
    }
}

Предложения о том, что добавить в универсальный метод, приветствуются.

4b9b3361

Ответ 1

Я бы, наверное, этого не сделал. Это может смутить пользователя, чтобы перечисление перечислило разные вещи в зависимости от ссылки на интерфейс, вызывающей его, и, конечно же, вопрос о том, что также возвращает общий доход.

Вместо этого вы можете просто вывести версию списков в режиме "только для чтения" как итератор:

public class ComponentModel
{
    public ComponentModel() { }

    private List<IComponentA> ListOfComponentA = new List<IComponentA>();
    private List<IComponentB> ListOfComponentB = new List<IComponentB>();

    public IEnumerable<IComponentA> AComponents 
    {
        get { return ListOfComponentA.Skip(0); }
    }

    public IEnumerable<IComponentB> BComponents
    {
        get { return ListOfComponentB.Skip(0); }
    }

    ...
}

Используя Skip(0), вы возвращаете итератор, и это предотвращает возврат к List<IComponentA> и изменение List из-под вас.

Вы также можете использовать ReadOnlyCollection, конечно, но это довольно неуклюжие, поскольку они бросают, когда вы пытаетесь сделать мутирующие операторы.

Итак, теперь вы можете выполнить итерацию:

foreach(var a in myModel.AComponents)
{
    ...
}

foreach(var b in myModel.BComponents)
{
    ...
}

Кроме того, IF списки компонентов A и B всегда имеют одинаковую длину, вы можете иметь перечислитель по сравнению с Tuple из них в .NET 4.0 и используя Метод Linq Zip():

public IEnumerable<Tuple<IComponetA, IComponetB>> Components
{
    get
    {
        return ListOfComponentA.Zip(ListOfComponentB, (a,b) => Tuple.Create(a,b));
    }
}

Ответ 2

Принятый ответ в точности правильный; вы должны реализовать вещи, которые имеют две последовательности, имеющие две последовательности, а не две последовательности.

Доказанная до сих пор причина, почему это плохая идея, достаточна, а именно, что она обычно менее запутанна, когда вы выбираете композицию над наследованием. Тем не менее, я считаю, что полезно отметить, что существует еще одна причина, по которой внедрение двух разных интерфейсов IEnumerable<T> - это действительно плохая идея: мы добавили общую ковариацию к IEnumerable<T> в С# 4.

Общая ковариантность означает, что вы можете передать череду черепах методу, который принимает последовательность животных, потому что черепахи - животные. Итак, что же тогда делает runtime в этом случае?

class Weird : IEnumerable<Turtle>, IEnumerable<Giraffe> { ... }
...
Weird weird = whatever;
foreach(Animal animal in weird)
{
    ...

Является ли animal черепахой или жирафом? Время выполнения должно выбрать один из них, и совершенно непонятно, какой из них выбрать. Это одна из редких ситуаций, когда спецификация С# не говорит, что происходит; это поведение, определяемое реализацией. Избегайте, избегайте, избегайте.

Ответ 3

Кошка - это не последовательность ног, глаз и бакенбардов. Скорее, у него есть последовательность ног, глаз и бакенбардов. Состав над наследованием.

Вместо того, чтобы ComponentModel считаться двумя полностью несвязанными последовательностями IComponentA и IComponentB, вы должны удалить это и просто добавить методы:

public IEnumerable<IComponentA> GetSequenceOfComponentA() { // }
public IEnumerable<IComponentB> GetSequenceOfComponentB() { // }

(Что вы ожидаете от foreach(var x in componentModel) { // }? Что происходит здесь: public void M(IEnumerable<IComponentA> a) { } и public void M(IEnumerable<IComponentB> b) { }, а затем M(componentModel)?)

Ответ 4

возможно, что-то в этом направлении будет работать. он выполняет публичный класс MyCollection (T): IEnumerable (T) использовать один и тот же тип для обработки двух разных типов данных

вы можете пойти дальше с MyCollection (T, X, Y, Z): IEnumerable (T) где X, Y, Z - списки ваших других типов за исключением того, что T будет вашим главным итератором, выполняющим внутреннюю итерацию ваших других списков параллельно.

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Start.....");
        for ( int i = 0; i  < 10; i++)
        {
            IEnumerable collection;

            Console.WriteLine();

            if (i % 2 == 0)
            { Console.Write("Ints..... ");  
              collection = new MyCollection<int>(new int[] { 1,2,3 });  }
            else
            { Console.Write("Strings..... ");  
              collection = new MyCollection<string>(new string[] { "x","y","z" } ); }

            foreach (var item in collection) { Console.Write(item); }
            Console.WriteLine();
        }
        Console.WriteLine(); Console.WriteLine("End....."); Console.ReadLine();
    }
}

public class MyCollection<T> : IEnumerable<T>
{
    private T[] _Tdata;
    public MyCollection4(T[] arrayOfT)
    {
        _Tdata = arrayOfT;
    }

    public IEnumerator<T> GetEnumerator() 
    {

        var listofT = new List<T>(_Tdata);
        return listofT.GetEnumerator();

    }

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